Сохранять каждое вхождение, найденное awk, в массив

Aug 20 2020

Мой предыдущий вопрос был помечен как "повторяющийся", и мне указали на то и это . Решения, представленные в этих потоках, вообще не решают этой проблемы.

Содержимое файла 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

Мне нужно извлечь строки «PATTERN1» и «PATTERN2» + между ними, и следующая команда отлично это делает:

awk '/ ШАБЛОН1 /, / ШАБЛОН2 /' ./file.txt

Вывод:

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

Но теперь я пытаюсь создать сценарий bash, который:

  1. использует awk для поиска строк между ШАБЛОН1 и ШАБЛОН2
  2. хранить каждое вхождение ШАБЛОН1 + строки между + ШАБЛОН2 в массиве
  3. делает 1 и 2 до конца файла.

Чтобы уточнить. Средство хранит в кавычках следующие строки:

"PATTERN1
Some line of text 3
Some line of text 4
Some line of text 5
PATTERN2"

к array[0]

и сохраните в кавычках следующие строки:

"PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2"

к array[1]

и так далее ..... если есть больше вхождений PATTERN1 и PATTERN2

Что у меня сейчас есть:

#!/bin/bash
var0=`cat ./file.txt`
mapfile -t thearray < <(echo "$var0" | awk '/PATTERN1 /,/PATTERN2/')

Вышеуказанное не работает.
И насколько это возможно, я не хочу использовать mapfile, потому что сценарий может выполняться в системе, которая его не поддерживает.

На основе этой ссылки предоставлено:

myvar=$(cat ./file.txt)
myarray=($(echo "$var0" | awk '/PATTERN1 /,/PATTERN2/')) 

Но когда я сделаю echo ${myarray[1]}

Я получаю пустой ответ.

И когда я это сделаю echo ${myarray[0]}

Я получил:

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

Чего я ожидаю, когда повторяю эхо ${myarray[0]}

PATTERN1
Some line of text 3
Some line of text 4
Some line of text 5
PATTERN2

Чего я ожидаю, когда это сделаю echo ${myarray[1]}

PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2

Любая помощь будет отличной.

Ответы

2 PaulHodges Aug 20 2020 at 02:12

Как предложил Чарльз ...

Отредактировано для удаления новой строки из и блока (не для каждой записи)

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)

Я его переформатировал. Это становилось все труднее читать.

И проверить это -

$: echo "[${array[1]}]"
[PATTERN1
Some line of text 9
Some line of text 10
Some line of text 11
PATTERN2]

В стороне, мне кажется очень странным включать избыточные контрольные значения в элементы данных, поэтому, если вы хотите их удалить:

$: 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

Простая реализация bashможет быть примерно такой:

#!/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

Беги как ./script < file. Использование awkне требуется, но сценарий также будет корректно работать на awkвыходе.

ZYXRhythm Aug 20 2020 at 12:43

Ответ Пола делает то, что я хочу, поэтому я пометил его как принятый. Хотя его решение создает пустую дополнительную строку внизу каждого сохраненного значения в массиве, что нормально, его все равно легко удалить, поэтому я не возражал. Но я также разместил этот же вопрос на другом сайте, и, хотя ответ Пола был хорош, я нашел лучшее решение:

IFS=$'\r' read -d'\r' -a ARR < <(awk '/PATTERN1/,/PATTERN2/ {if($0 ~ /PATTERN2/) printf $0"\r"; else print}' file.txt)

Вышеупомянутое выполняет свою работу, не создает пустую дополнительную строку, а это один лайнер.

echo "${ARR[1]}"
echo "${ARR[0]}"

Вывод:

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