Я читаю файл построчно. Каждая строка выглядит так:
xxyu: JHYU_IOPI
Каждая строка передается в awk, как показано ниже. Я хочу вывести предыдущую строку совпадающего шаблона; я могу сделать это с помощью grep и хочу узнать, где я сделал ошибку с помощью awk.
#!/bin/bash
while read i
do
awk '/$i/{print a}{a=$0}' ver_in.txt
done<in.txt
Я также попробовал это:
#!/bin/bash
while read i
do
awk -v var="$i" '/var/{print a}{a=$0}' jil.txt
done<in.txt
Редактировать: использую awk после того, как получил предложение не использовать sh read. Мой ввод и желаемый вывод показаны ниже:
EDIT 1: отредактировал входные данные для скрипта awk @Ed Morton, как показано ниже
Входной файл: cat-файл
/* ----------------- AIX_RUN_WATCH ----------------- */
insert_job: AIX_RUN_WATCH job_type: BOX
owner: root
permission:
date_conditions: 1
days_of_week: su
start_times: "22:00"
alarm_if_fail: 1
alarm_if_terminated: 1
group: app
send_notification: 0
notification_emailaddress:
/* ----------------- AIX_stop ----------------- */
insert_job: AIXstop job_type: CMD
box_name: AIX_RUN_WATCH
command: ls
machine: cfg.mc
owner: root
permission:
date_conditions: 0
box_terminator: 1
std_out_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stdout"
std_err_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stderr"
alarm_if_fail: 1
alarm_if_terminated: 1
group: app
send_notification: 1
/* ----------------- AIX_start ----------------- */
insert_job: AIX_start job_type: CMD
box_name: AIX_RUN_WATCH
command: ls
machine: cfg.mc
owner: root
permission:
date_conditions: 0
box_terminator: 1
std_out_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stdout"
std_err_file: ">> /tmp/${AUTOSERV}.${AUTO_JOB_NAME}.$(date +%Y%m%d).stderr"
alarm_if_fail: 1
alarm_if_terminated: 1
group: app
cat targets
box_name: AIX_RUN_WATCH
Ожидаемый результат -
box_name: AIX_RUN_WATCH
insert_job: AIX_stop
insert_job: AIX_start
решение1
Для первой попытки вам нужно использовать двойные кавычки для расширения переменной оболочки, а затем экранировать двойные кавычки для $
оператора awk, чтобы предотвратить его расширение оболочкой, но учтите, что такое использование приведет к сбою awk в случае, если переменная $i
содержит специальный символ, например \
, /
. [Я пропускаю этот шаг, чтобы исправить одну или несколько других проблем с вашей командой].
while read i
do
awk "/$i/{print a}{a=\$0}" ver_in.txt
done<in.txt
для второй попытки вам нужно использовать либо сопоставление с регулярным выражением, либо сопоставление со строкой для текущей строки, например, используя сопоставление с регулярным выражением (частичное сопоставление с регулярным выражением) с помощью:
while read i
do
awk -v var="$i" '$0 ~ var{print a}{a=$0}' jil.txt
done<in.txt
или совпадение строки (полное совпадение строки), например:
while read i
do
awk -v var="$i" '$0==var{print a}{a=$0}' jil.txt
done<in.txt
Теперь, говоря о командах, которые вы пытаетесь использовать для вывода предыдущей строки совпадающего шаблона, вы можете сделать все это с помощью awk и остановиться, используя цикл оболочки; здесь мы выполняем полное совпадение строки:
awk 'NR==FNR { str[$0]; next }
($0 in str) && prev!="" { print prev } { prev=$0 }' in.txt ver_in.txt
или выполняя частичное сопоставление с регулярным выражением:
awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ ptrn && prev!="") print prev; prev=$0 }' in.txt ver_in.txt
или выполнить частичное совпадение строки:
awk 'NR==FNR { strings[$0]; next }
{ for(str in strings) if(index($0, str) && prev!="") print prev; prev=$0 }' in.txt ver_in.txt
или выполняя полное сопоставление с регулярным выражением:
awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ "^"ptrn"$" && prev!="") print prev; prev=$0 }' in.txt ver_in.txt
решение2
Для этого вам не нужен цикл while read, а обработка текста в sh — плохая идея (см.Почему использование цикла оболочки для обработки текста считается плохой практикой?).
Вместо этого заставьте свой скрипт awk обрабатывать оба файла.
awk 'NR==FNR { re = $0 "|" re ; next}; # append input line and | to re
FNR == 1 { sub(/\|$/,"",re) }; # remove trailing | on 1st line of 2nd file
$0 ~ re { print a }; # if the current line matches re, print a
{a = $0}' in.txt ver_in.txt
При чтении первого файла ( in.txt
) он создает регулярное выражение в переменной, вызываемой re
путем добавления каждой входной строки и регулярного выражения «чередование» (т.е.ИЛИ) оператор.
Когда он закончит читать первый файл, первое, что ему нужно сделать, это удалить завершающий символ |
из re
. Это необходимо, потому что re
будетвсегдав конечном итоге с |
символом из-за способа его построения. Если мы не удалим его, этот хвостик |
заставит регулярное выражение сопоставляться с каждой строкой ver_in.txt
.
После этого выведите переменную a
, если текущая входная строка соответствует регулярному выражению в переменной re
(это выведет пустую строку, если совпадает первая строка ver_in.txt re
, поскольку a пуста. Если вы не хотите, чтобы это произошло, измените эту строку с $0 ~ re {print a}
на $0 ~ re && a != "" {print a}
).
Затем, независимо от того, совпадает ли он или нет, установите a=$0
.
ПРИМЕЧАНИЕ: NR==FNR {... ; next}
это очень распространенная идиома awk для обработки первого входного файла иным образом, чем второго и последующих входных файлов. NR
— это глобальный счетчик строк для всех считываемых файлов, а FNR
— счетчик строк для текущего файла... поэтому, если NR==FNR
, это означает, что мы считываем первый файл. Оператор next
переходит к следующей входной строке, предотвращая выполнение оставшейся части скрипта awk в первом файле.
Вы не предоставили полный образец данных, поэтому я сделал свой собственный для тестирования:
$ cat in.txt
xxyu: JHYU_IOPI
foo
bar
Этот файл in.txt приведет к тому, что re станет равнымbar|foo|xxyu: JHYU_IOPI
Кстати, поскольку скрипт awk выполняет сопоставление регулярных выражений с re
, строки в in.txt
обрабатываются как регулярные выражения, а не как фиксированный текст. Это означает, что если вы хотите, чтобы любые специальные символы регулярных выражений (например .
, |
, [
или ]
и многие другие) в in.txt обрабатывались как литеральные символы, вам нужно будет экранировать их с помощью обратной косой черты... вам пришлось бы сделать это и с вашим исходным циклом sh+awk.
$ cat ver_in.txt
a line 1
xxyu: JHYU_IOPI
b line 3
d line 4
bar
e line 6
f line 7
foo
Вывод скрипта awk выше:
a line 1
d line 4
f line 7
решение3
Не используйте цикл оболочки для манипулирования текстом, см.Почему использование цикла оболочки для обработки текста считается плохой практикой?. Люди, которые изобрели shell, также изобрели awk, чтобы shell мог вызывать его для работы с текстом.
Использование любого awk в любой оболочке на любой машине Unix:
$ cat tst.awk
NR==FNR {
tgts[$0]
next
}
$0 in tgts {
if ( $0 != prevTgt ) {
print $0
prevTgt = $0
}
print prevLine
}
{ prevLine = $1 FS $2 }
$ awk -f tst.awk targets file
box_name: AIX_RUN_WATCH
insert_job: AIXstop
insert_job: AIX_start
Оригинальный ответ:
awk '
BEGIN { RS=""; FS="\n" }
$2 != prev {
print $2
prev = $2
}
{ print $1 }
' file
ght: ertyjk
xxx: rtyuiol
xxx: ertyuikl_fghjk
xxx: qwertyujkl
xxx: rtyuiol_123
ght: YUIOPO
xxx: rtyuiol
xxx: rtyuiopfghj
xxx: dfghjkvbnm
xxx: qzdfghnbvfgh
xxx: qsxcvghuiokmnhgf
Видетьhttps://www.gnu.org/software/gawk/manual/gawk.html#Многострочныйдля того, как установка RS в значение null позволяет нам работать с многострочными записями, а затем установка FS в значение новой строки означает, что каждое поле в такой записи представляет собой целую строку, поэтому мы обрабатываем ваши данные как записи, разделенные пустыми строками, каждая из которых содержит 2 строки данных.
Вы упомянули, что у вас есть какой-то другой файл ght-строк, который указывает, что должно быть напечатано, подразумевая, что есть другие блоки, которые не должны быть напечатаны. Если у вас есть такой файл, и он выглядит так:
$ cat targets
ght: ertyjk
ght: YUIOPO
и ваш другой входной файл содержит некоторые ght:
строки, которые не соответствуют приведенным выше, например, см. ght: whatever
блоки в измененном входном файле ниже:
$ cat file
xxx: rtyuiol
ght: ertyjk
xxx: ertyuikl_fghjk
ght: ertyjk
xxx: qwertyujkl
ght: ertyjk
xxx: rtyuiol_123
ght: ertyjk
xxx: foo
ght: whatever
xxx: bar
ght: whatever
xxx: rtyuiol
ght: YUIOPO
xxx: rtyuiopfghj
ght: YUIOPO
xxx: dfghjkvbnm
ght: YUIOPO
xxx: qzdfghnbvfgh
ght: YUIOPO
xxx: qsxcvghuiokmnhgf
ght: YUIOPO
то приведенный выше код будет обновлен следующим образом:
awk '
BEGIN { FS="\n" }
NR==FNR {
tgts[$0]
next
}
$2 != prev {
if ( inTgts = ($2 in tgts) ) {
print $2
}
prev = $2
}
inTgts { print $1 }
' targets RS='' file
ght: ertyjk
xxx: rtyuiol
xxx: ertyuikl_fghjk
xxx: qwertyujkl
xxx: rtyuiol_123
ght: YUIOPO
xxx: rtyuiol
xxx: rtyuiopfghj
xxx: dfghjkvbnm
xxx: qzdfghnbvfgh
xxx: qsxcvghuiokmnhgf