Variable wird beim Übergeben aus einer Datei in awk nicht akzeptiert

Variable wird beim Übergeben aus einer Datei in awk nicht akzeptiert

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 $iSonderzeichen 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, reindem 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, redastetsaufgrund 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. NRist der globale Zeilenzähler für alle gelesenen Dateien und FNRist der Zeilenzähler für die aktuelle Datei....also wenn NR==FNR, bedeutet dies, dass wir die erste Datei lesen. Die nextAnweisung 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.txtals 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: whateverBlö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

verwandte Informationen