Memorizza ogni occorrenza trovata da awk in un array
La mia domanda precedente è stata contrassegnata come "duplicata" e mi è stato indicato questo e questo . Le soluzioni fornite su quei thread non risolvono affatto questo problema.
Contenuto di file.txt:
Some line of text 0
Some line of text 1
Some line of text 2
PATTERN1
Some line of text 3
Some line of text 4
Some line of text 5
PATTERN2
Some line of text 6
Some line of text 7
Some line of text 8
PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2
Some line of text 12
Some line of text 13
Some line of text 14
Devo estrarre "PATTERN1" e "PATTERN2" + linee intermedie e il seguente comando lo fa perfettamente:
awk '/ PATTERN1 /, / PATTERN2 /' ./file.txt
Produzione:
PATTERN1 Some line of text 3 Some line of text 4 Some line of text 5 PATTERN2 PATTERN1 Some line of text 9 Some line of text 10 Some line of text 11 PATTERN2
Ma ora sto cercando di creare uno script bash che:
- usa awk per trovare le linee tra PATTERN1 e PATTERN2
- memorizzare ogni occorrenza di PATTERN1 + righe tra + PATTERN2 in un array
- fa 1 e 2 fino alla fine del file.
Per chiarire. Significa memorizzare le seguenti righe tra virgolette:
"PATTERN1
Some line of text 3
Some line of text 4
Some line of text 5
PATTERN2"
per array[0]
e memorizza le seguenti righe tra virgolette:
"PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2"
per array[1]
e così via ..... se ci sono più occorrenze di PATTERN1 e PATTERN2
Quello che ho attualmente:
#!/bin/bash
var0=`cat ./file.txt`
mapfile -t thearray < <(echo "$var0" | awk '/PATTERN1 /,/PATTERN2/')
Quanto sopra non funziona.
E per quanto possibile non voglio usare mapfile, perché lo script potrebbe essere eseguito su un sistema che non lo supporta.
Sulla base di questo collegamento fornito:
myvar=$(cat ./file.txt)
myarray=($(echo "$var0" | awk '/PATTERN1 /,/PATTERN2/'))
Ma quando lo faccio echo ${myarray[1]}
Ricevo una risposta vuota.
E quando lo faccio echo ${myarray[0]}
Ottengo:
PATTERN1 Some line of text 3 Some line of text 4 Some line of text 5 PATTERN2 PATTERN1 Some line of text 9 Some line of text 10 Some line of text 11 PATTERN2
Quello che mi aspetto quando faccio eco ${myarray[0]}
PATTERN1 Some line of text 3 Some line of text 4 Some line of text 5 PATTERN2
Cosa mi aspetto quando lo faccio echo ${myarray[1]}
PATTERN1 Some line of text 9 Some line of text 10 Some line of text 11 PATTERN2
Qualsiasi aiuto sarà gradito.
Risposte
Come suggerì Charles ...
Modificato per rimuovere la nuova riga dal blocco e dal blocco (non tutti i record)
while IFS= read -r -d '' x; do array+=("$x"); done < <(awk ' /PATTERN1/,/PATTERN2/ { if ( $0 ~ "PATTERN2" ) { x=$0; printf "%s%c",x,0; next }
print }' ./file.txt)
L'ho riformattato. Stava diventando piuttosto impegnativo e difficile da leggere.
E per provarlo -
$: echo "[${array[1]}]"
[PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2]
Per inciso, mi sembra molto strano includere i valori sentinella ridondanti negli elementi dei dati, quindi se vuoi rimuoverli:
$: while IFS= read -r -d '' x; do array+=("$x"); done < <( awk '/PATTERN1/,/PATTERN2/{ if ( $0 ~ "PATTERN1" ) { next }
if ( $0 ~ "PATTERN2" ) { len--; for (l in ary) { printf "%s%c", ary[l], l<len ? "\n" : 0; } delete ary; len=0; next } ary[len++]=$0;
}' ./file.txt )
$: echo "[${array[1]}]"
[Some line of text 9
Some line of text 10
Some line of text 11]
Un'implementazione in chiaro bash
potrebbe essere qualcosa del genere:
#!/bin/bash
beginpat='PATTERN1'
endpat='PATTERN2'
array=()
n=-1
inpatterns=
while read -r; do
if [[ ! $inpatterns && $REPLY = $beginpat ]]; then array[++n]=$REPLY
inpatterns=1
elif [[ $inpatterns ]]; then array[n]+=$'\n'$REPLY if [[ $REPLY = $endpat ]]; then inpatterns= fi fi done # Report captured lines for ((i = 0; i <= n; ++i)); do printf "=== array[%d] ===\n%s\n\n" $i "${array[i]}"
done
Esegui come ./script < file
. Non awk
è richiesto l' uso di ma lo script funzionerà correttamente anche awk
sull'output.
La risposta di Paul fa quello che voglio, quindi l'ho contrassegnata come risposta accettata. Sebbene la sua soluzione produca una riga aggiuntiva vuota alla fine di ogni valore memorizzato nell'array, il che è ok, è comunque facile da rimuovere, quindi non mi dispiace. Ma ho anche pubblicato la stessa domanda su un altro sito e, sebbene la risposta di Paul fosse buona, ho trovato una soluzione migliore:
IFS=$'\r' read -d'\r' -a ARR < <(awk '/PATTERN1/,/PATTERN2/ {if($0 ~ /PATTERN2/) printf $0"\r"; else print}' file.txt)
Quanto sopra fa il lavoro, non produce una riga extra vuota ed è una riga.
echo "${ARR[1]}"
echo "${ARR[0]}"
Produzione:
PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2
PATTERN1
Some line of text 3
Some line of text 4
Some line of text 5
PATTERN2