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 $i
contenha 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 re
anexando 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 re
será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 a
se 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 next
instruçã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.txt
sã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: whatever
blocos 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