Almacene cada ocurrencia encontrada por awk en una matriz

Aug 20 2020

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:

  1. usa awk para encontrar las líneas entre PATTERN1 y PATTERN2
  2. almacenar cada aparición de PATTERN1 + líneas entre + PATTERN2 en una matriz
  3. 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

2 PaulHodges Aug 20 2020 at 02:12

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]
3 M.NejatAydin Aug 20 2020 at 00:44

Una implementación en plano bashpodrí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. awkNo se requiere el uso de, pero el script también funcionará correctamente en la awksalida.

ZYXRhythm Aug 20 2020 at 12:43

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