Almacene cada ocurrencia encontrada por awk en una matriz
Mi pregunta anterior fue marcada como "duplicada" y me señalaron esto y esto . Las soluciones proporcionadas en esos hilos no resuelven esto en absoluto.
Contenido de 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
Necesito extraer "PATTERN1" y "PATTERN2" + líneas intermedias, y el siguiente comando lo hace perfectamente:
awk '/ PATTERN1 /, / PATTERN2 /' ./file.txt
Salida:
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
Pero ahora estoy tratando de crear un script bash que:
- usa awk para encontrar las líneas entre PATTERN1 y PATTERN2
- almacenar cada aparición de PATTERN1 + líneas entre + PATTERN2 en una matriz
- hace 1 y 2 hasta el final del archivo.
Para aclarar. Significa almacenar las siguientes líneas dentro de las comillas:
"PATTERN1
Some line of text 3
Some line of text 4
Some line of text 5
PATTERN2"
a array[0]
y almacene las siguientes líneas dentro de las comillas:
"PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2"
a array[1]
y así sucesivamente ..... si hay más ocurrencias de PATTERN1 y PATTERN2
Lo que tengo actualmente:
#!/bin/bash
var0=`cat ./file.txt`
mapfile -t thearray < <(echo "$var0" | awk '/PATTERN1 /,/PATTERN2/')
Lo anterior no funciona.
Y en la medida de lo posible, no quiero usar mapfile, porque el script podría ejecutarse en un sistema que no lo admite.
Basado en este enlace proporcionado:
myvar=$(cat ./file.txt)
myarray=($(echo "$var0" | awk '/PATTERN1 /,/PATTERN2/'))
Pero cuando lo hago echo ${myarray[1]}
Recibo una respuesta en blanco.
Y cuando lo hago echo ${myarray[0]}
Yo obtengo:
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
Lo que espero cuando hago eco ${myarray[0]}
PATTERN1 Some line of text 3 Some line of text 4 Some line of text 5 PATTERN2
Lo que espero cuando lo hago echo ${myarray[1]}
PATTERN1 Some line of text 9 Some line of text 10 Some line of text 11 PATTERN2
Cualquier ayuda será genial.
Respuestas
Como sugirió Charles ...
Editado para quitar la nueva línea de y del bloque (no todos los registros)
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)
Lo reformateé. Se estaba volviendo un poco ocupado y difícil de leer.
Y para probarlo -
$: echo "[${array[1]}]"
[PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2]
Aparte, me parece muy extraño incluir los valores centinela redundantes en los elementos de datos, por lo que si desea eliminarlos:
$: 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]
Una implementación en plano bash
podría ser algo así:
#!/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
Ejecutar como ./script < file
. awk
No se requiere el uso de, pero el script también funcionará correctamente en la awk
salida.
La respuesta de Paul hace lo que quiero, así que la marqué como la respuesta aceptada. Aunque su solución produce una línea adicional en blanco en la parte inferior de cada valor almacenado en la matriz, lo cual está bien, es fácil de eliminar de todos modos, así que no me importó. Pero también publiqué esta misma pregunta en otro sitio, y aunque la respuesta de Paul fue buena, encontré una mejor solución:
IFS=$'\r' read -d'\r' -a ARR < <(awk '/PATTERN1/,/PATTERN2/ {if($0 ~ /PATTERN2/) printf $0"\r"; else print}' file.txt)
Lo anterior hace el trabajo, no produce una línea adicional en blanco y es una línea.
echo "${ARR[1]}"
echo "${ARR[0]}"
Salida:
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