Ich lese eine Datei Zeile für Zeile. Jede Zeile sieht so aus:
xxyu: JHYU_IOPI
Jede Zeile wird wie unten an awk übergeben. Ich möchte die vorherige Zeile des passenden Musters drucken; das kann ich mit grep erreichen und möchte wissen, wo ich mit awk einen Fehler gemacht habe.
#!/bin/bash
while read i
do
awk '/$i/{print a}{a=$0}' ver_in.txt
done<in.txt
Ich habe auch Folgendes versucht:
#!/bin/bash
while read i
do
awk -v var="$i" '/var/{print a}{a=$0}' jil.txt
done<in.txt
Bearbeiten: Ich verwende awk, nachdem mir empfohlen wurde, sh read nicht zu verwenden. Meine Eingabe und die gewünschte Ausgabe werden unten angezeigt:
BEARBEITEN 1: Die Eingabe für das @Ed Morton-AWK-Skript wurde wie folgt bearbeitet
Eingabedatei: Cat-Datei
/* ----------------- 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
Erwartete Ausgabe -
box_name: AIX_RUN_WATCH
insert_job: AIX_stop
insert_job: AIX_start
Antwort1
beim ersten Versuch müssen Sie doppelte Anführungszeichen für die Shell-Variablenerweiterung verwenden und dann die Anführungszeichen für den awk- $
Operator escapen, um eine Erweiterung durch die Shell zu verhindern. Beachten Sie jedoch, dass eine solche Verwendung awk unterbricht, falls die Variable $i
Sonderzeichen wie \
, enthält /
. [Ich überspringe jetzt das Beheben eines oder mehrerer weiterer Probleme mit Ihrem Befehl].
while read i
do
awk "/$i/{print a}{a=\$0}" ver_in.txt
done<in.txt
für den zweiten Versuch müssen Sie entweder Regex-Match oder String-Match für die aktuelle Zeile verwenden, wie beispielsweise bei Verwendung von Regex-Match (teilweiser Regex-Match) mit:
while read i
do
awk -v var="$i" '$0 ~ var{print a}{a=$0}' jil.txt
done<in.txt
oder String-Übereinstimmung (vollständige String-Übereinstimmung) wie:
while read i
do
awk -v var="$i" '$0==var{print a}{a=$0}' jil.txt
done<in.txt
Wenn wir nun über die Befehle sprechen, versuchen Sie, sie so zu verwenden, dass die vorherige Zeile des übereinstimmenden Musters gedruckt wird. Sie können alles mit awk tun und mit der Shell-Schleife aufhören. Hier führen wir eine vollständige Zeichenfolgenübereinstimmung durch:
awk 'NR==FNR { str[$0]; next }
($0 in str) && prev!="" { print prev } { prev=$0 }' in.txt ver_in.txt
oder teilweise Regex-Übereinstimmung durchführen:
awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ ptrn && prev!="") print prev; prev=$0 }' in.txt ver_in.txt
oder durch teilweisen String-Abgleich:
awk 'NR==FNR { strings[$0]; next }
{ for(str in strings) if(index($0, str) && prev!="") print prev; prev=$0 }' in.txt ver_in.txt
oder vollständige Regex-Übereinstimmung durchführen:
awk 'NR==FNR { patt[$0]; next }
{ for(ptrn in patt) if($0 ~ "^"ptrn"$" && prev!="") print prev; prev=$0 }' in.txt ver_in.txt
Antwort2
Sie benötigen hierfür keine while-Leseschleife und die Textverarbeitung in sh ist keine gute Idee (sieheWarum gilt die Verwendung einer Shell-Schleife zur Textverarbeitung als schlechte Praxis?).
Lassen Sie stattdessen Ihr Awk-Skript beide Dateien verarbeiten.
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
Beim Lesen der ersten Datei ( in.txt
) wird ein regulärer Ausdruck in einer aufgerufenen Variable erstellt, re
indem an jede Eingabezeile der reguläre Ausdruck "Alternation" angehängt wird (alsoODER) Operator.
Wenn die erste Datei gelesen wurde, muss als erstes der letzte Teil |
von entfernt werden re
. Dies ist notwendig, re
dastetsaufgrund der Art und Weise, wie es aufgebaut ist, mit einem Zeichen enden |
. Wenn wir es nicht entfernen, |
führt dieses Nachstellen dazu, dass der reguläre Ausdruck mit jeder Zeile von übereinstimmt ver_in.txt
.
Drucken Sie anschließend die Variable a
, wenn die aktuelle Eingabezeile mit dem regulären Ausdruck in der Variable übereinstimmt re
(dadurch wird eine leere Zeile gedruckt, wenn die erste Zeile von ver_in.txt übereinstimmt re
– weil a leer ist. Wenn Sie dies nicht möchten, ändern Sie diese Zeile von $0 ~ re {print a}
in $0 ~ re && a != "" {print a}
).
Legen Sie dann fest, ob es übereinstimmt oder nicht a=$0
.
HINWEIS: Dies NR==FNR {... ; next}
ist ein sehr gebräuchliches awk-Idiom, bei dem die erste Eingabedatei anders behandelt wird als die zweite und die folgenden Eingabedateien. NR
ist der globale Zeilenzähler für alle gelesenen Dateien und FNR
ist der Zeilenzähler für die aktuelle Datei....also wenn NR==FNR
, bedeutet dies, dass wir die erste Datei lesen. Die next
Anweisung springt zur nächsten Eingabezeile und verhindert, dass der Rest des awk-Skripts ausgeführt wird, während es sich in der ersten Datei befindet.
Sie haben keine vollständige Datenprobe bereitgestellt, deshalb habe ich zum Testen meine eigene erstellt:
$ cat in.txt
xxyu: JHYU_IOPI
foo
bar
Diese in.txt-Datei bewirkt, dass re gleichbar|foo|xxyu: JHYU_IOPI
Übrigens, da das awk-Skript einen Regex-Abgleich mit durchführt re
, werden die Zeilen in in.txt
als reguläre Ausdrücke und nicht als fester Text behandelt. Das bedeutet, dass Sie Regex-Sonderzeichen (wie .
, |
, [
oder ]
und viele andere) in in.txt mit einem Backslash maskieren müssen, wenn Sie möchten, dass sie als Literalzeichen behandelt werden....das hätten Sie auch mit Ihrer ursprünglichen sh+awk-Schleife tun müssen.
$ cat ver_in.txt
a line 1
xxyu: JHYU_IOPI
b line 3
d line 4
bar
e line 6
f line 7
foo
Ausgabe des obigen Awk-Skripts:
a line 1
d line 4
f line 7
Antwort3
Verwenden Sie keine Shell-Schleife zur Textmanipulation.Warum gilt die Verwendung einer Shell-Schleife zur Textverarbeitung als schlechte Praxis?. Die Leute, die Shell erfunden haben, haben auch Awk erfunden, damit Shell es aufrufen und Text bearbeiten kann.
Verwenden eines beliebigen awk in einer beliebigen Shell auf jeder Unix-Box:
$ 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
Ursprüngliche Antwort:
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
Sehenhttps://www.gnu.org/software/gawk/manual/gawk.html#Multiple-LineWenn wir RS auf Null setzen, können wir mit mehrzeiligen Datensätzen arbeiten. Wenn wir FS dann auf eine neue Zeile setzen, bedeutet das, dass jedes Feld in einem solchen Datensatz eine ganze Zeile ist. Daher behandeln wir Ihre Daten als durch Leerzeilen getrennte Datensätze, die jeweils zwei Datenzeilen enthalten.
Sie haben erwähnt, dass Sie eine andere Datei mit Zeilen haben, die angeben, welche gedruckt werden sollen, was bedeutet, dass es andere Blöcke gibt, die nicht gedruckt werden sollen. Wenn Sie eine solche Datei haben und sie so aussieht:
$ cat targets
ght: ertyjk
ght: YUIOPO
und Ihre andere Eingabedatei enthält einige ght:
Zeilen, die nicht mit den obigen übereinstimmen, siehe beispielsweise die ght: whatever
Blöcke in der geänderten Eingabedatei unten:
$ 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
dann würde der obige Code wie folgt aktualisiert:
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