Variável não aceita no awk ao passar de um arquivo

Variável não aceita no awk ao passar de um arquivo

Estou lendo um arquivo linha por linha. Cada linha se parece com isto:

xxyu: JHYU_IOPI

Cada linha é passada para o awk conforme abaixo. Quero imprimir a linha anterior do padrão correspondente; Posso conseguir isso com o grep e quero saber onde cometi um erro com o awk.

#!/bin/bash
while read i
do
 awk '/$i/{print a}{a=$0}' ver_in.txt
done<in.txt

Eu também tentei isso:

#!/bin/bash
while read i
do
 awk -v var="$i" '/var/{print a}{a=$0}' jil.txt
done<in.txt

Editar: usando o awk depois de receber sugestão para não usar sh read. Minha entrada e saída desejada são mostradas abaixo:

EDIT 1: editou a entrada para o script @Ed Morton awk conforme abaixo

Arquivo de entrada: arquivo 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

Resultado esperado -

 box_name: AIX_RUN_WATCH
 insert_job: AIX_stop
 insert_job: AIX_start

Responder1

para a primeira tentativa, você precisa usar aspas duplas para expansão de variável do shell e, em seguida, escapar daquelas do $operador awk para evitar que ele se expanda pelo shell, mas esteja ciente de que usar assim quebrará o awk caso a variável $icontenha caracteres especiais como \, /. [Estou pulando para corrigir um ou mais problemas com seu comando agora].

while read i
do
 awk "/$i/{print a}{a=\$0}" ver_in.txt
done<in.txt

para a segunda tentativa, você precisa usar a correspondência de regex ou a correspondência de string na linha atual, como usar a correspondência de regex (correspondência parcial de regex) com:

while read i
do
 awk -v var="$i" '$0 ~ var{print a}{a=$0}' jil.txt
done<in.txt

ou correspondência de string (correspondência de string completa) como:

while read i
do
 awk -v var="$i" '$0==var{print a}{a=$0}' jil.txt
done<in.txt

agora, falando sobre os comandos que você está tentando usar para imprimir a linha anterior do padrão correspondente, você pode fazer tudo com awk e parar usando o loop de shell; aqui estamos fazendo uma correspondência completa de string:

awk 'NR==FNR { str[$0]; next }
($0 in str) && prev!="" { print prev } { prev=$0 }' in.txt ver_in.txt

ou fazendo correspondência parcial de regex:

awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ ptrn && prev!="") print prev; prev=$0 }' in.txt ver_in.txt

ou fazendo correspondência parcial de string:

awk 'NR==FNR { strings[$0]; next }
{ for(str in strings) if(index($0, str) && prev!="") print prev; prev=$0 }' in.txt ver_in.txt

ou fazendo correspondência completa de regex:

awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ "^"ptrn"$" && prev!="") print prev; prev=$0 }' in.txt ver_in.txt

Responder2

Você não precisa de um loop while read para isso, e fazer processamento de texto em sh é uma má ideia (vejaPor que usar um loop de shell para processar texto é considerado uma prática inadequada?).

Em vez disso, faça com que seu script awk processe os dois arquivos.

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

Ao ler o primeiro arquivo ( in.txt), ele cria uma expressão regular em uma variável chamada reanexando cada linha de entrada e a "alternação" da regex (ou seja,OU) operador.

Quando terminar de ler o primeiro arquivo, a primeira coisa que você precisa fazer é remover o final |de re. Isto é necessário porque reserásempreacaba com |caráter pela forma como é construído. Se não o removermos, esse final |fará com que o regex corresponda a todas as linhas de ver_in.txt.

Depois disso, imprima a variável ase a linha de entrada atual corresponder ao regex na variável re(isso imprimirá uma linha vazia se a primeira linha de ver_in.txt corresponder re- porque a está vazio. Se você não quiser que isso aconteça, altere essa linha de $0 ~ re {print a}para $0 ~ re && a != "" {print a}).

Então, independentemente de corresponder ou não, defina a=$0.

NOTA: NR==FNR {... ; next}é um idioma awk muito comum para lidar com o primeiro arquivo de entrada de uma maneira diferente do segundo e dos arquivos de entrada subsequentes. NRé o contador de linha global para todos os arquivos que estão sendo lidos e FNRé o contador de linha do arquivo atual... então, se NR==FNR, isso significa que estamos lendo o primeiro arquivo. A nextinstrução pula para a próxima linha de entrada, evitando que o restante do script awk seja executado enquanto estiver no primeiro arquivo.

Você não forneceu uma amostra de dados completa, então fiz a minha própria para testar:

$ cat in.txt 
xxyu: JHYU_IOPI
foo
bar

Este arquivo in.txt fará com que re seja igualbar|foo|xxyu: JHYU_IOPI

Aliás, como o script awk está fazendo uma correspondência de regex com re, as linhas in.txtsão tratadas como expressões regulares, não como texto fixo. Isso significa que se você quiser que qualquer caractere especial regex (como ., |, [ou ]e muitos outros) em in.txt seja tratado como caracteres literais, você precisará escapá-los com uma barra invertida... você teria que fazer isso com seu loop sh + awk original também.

$ cat ver_in.txt 
a line 1
xxyu: JHYU_IOPI
b line 3
d line 4
bar
e line 6
f line 7
foo

Saída do script awk acima:

a line 1
d line 4
f line 7

Responder3

Não use um shell loop para manipular texto, vejaPor que usar um loop de shell para processar texto é considerado uma prática inadequada?. As pessoas que inventaram o shell também inventaram o awk para que o shell chame para manipular texto.

Usando qualquer awk em qualquer shell em cada caixa 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

Resposta original:

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

Verhttps://www.gnu.org/software/gawk/manual/gawk.html#Multiple-Linepara saber como definir RS como nulo nos permite trabalhar com registros de várias linhas e, em seguida, definir FS como uma nova linha significa que cada campo desse registro é uma linha inteira, portanto estamos tratando seus dados como registros separados por linhas em branco, cada um dos quais contém 2 linhas de dados.

Você mencionou ter algum outro arquivo de linhas de combate que indica quais devem ser impressas, o que implica que existem outros blocos que não devem ser impressos. Se você tiver esse arquivo e estiver assim:

$ cat targets
ght: ertyjk
ght: YUIOPO

e seu outro arquivo de entrada contém algumas ght:linhas que não correspondem às acima, por exemplo, veja os ght: whateverblocos no arquivo de entrada modificado abaixo:

$ 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

então o código acima seria atualizado para:

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

informação relacionada