Сохранять каждое вхождение, найденное awk, в массив
Мой предыдущий вопрос был помечен как "повторяющийся", и мне указали на то и это . Решения, представленные в этих потоках, вообще не решают этой проблемы.
Содержимое файла 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, который:
- использует awk для поиска строк между ШАБЛОН1 и ШАБЛОН2
- хранить каждое вхождение ШАБЛОН1 + строки между + ШАБЛОН2 в массиве
- делает 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
Любая помощь будет отличной.
Ответы
Как предложил Чарльз ...
Отредактировано для удаления новой строки из и блока (не для каждой записи)
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]
Простая реализация 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
выходе.
Ответ Пола делает то, что я хочу, поэтому я пометил его как принятый. Хотя его решение создает пустую дополнительную строку внизу каждого сохраненного значения в массиве, что нормально, его все равно легко удалить, поэтому я не возражал. Но я также разместил этот же вопрос на другом сайте, и, хотя ответ Пола был хорош, я нашел лучшее решение:
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