Переменная не принимается в awk при передаче из файла

Переменная не принимается в awk при передаче из файла

Я читаю файл построчно. Каждая строка выглядит так:

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

Связанный контент