Übergeben Sie die Shell-Variable als / pattern / an awk

59

Folgendes in einer meiner Shell-Funktionen haben:

function _process () {
  awk -v l="$line" '
  BEGIN {p=0}
  /'"$1"'/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '
}

Wenn also as aufgerufen wird _process $arg, $argwird as übergeben $1und als Suchmuster verwendet. Es funktioniert auf diese Weise, weil sich die Schale $1anstelle des awk-Musters ausdehnt ! Auch lkönnen innerhalb awk - Programm verwendet werden, wobei erklärt mit -v l="$line". Alles gut.

Ist es auf die gleiche Weise möglich, Suchmuster als Variable anzugeben?

Folgendes wird nicht funktionieren,

awk -v l="$line" -v search="$pattern" '
  BEGIN {p=0}
  /search/ {p=1}
  END{ if(p) print l >> "outfile.txt" }
  '

, as awk wird nicht /search/als Variable interpretiert , sondern wörtlich.

branquito
quelle

Antworten:

46

Verwenden Sie den ~Operator awk , und Sie müssen auf der rechten Seite keinen wörtlichen regulären Ausdruck angeben:

function _process () {
    awk -v l="$line" -v pattern="$1" '
        $0 ~ pattern {p=1} 
        END {if(p) print l >> "outfile.txt"}
    '  
}

Dies wäre zwar effizienter (muss nicht die gesamte Datei lesen)

function _process () {
    grep -q "$1" && echo "$line"
}

Je nach Muster kann es sein, wollen grep -Eq "$1"

Glenn Jackman
quelle
Dies ist genau das, was ich wollte (1. Beispiel), weil es die Semantik beibehält, die mein Ziel war. Vielen Dank.
Branquito
1
Ich habe die Entfernung des BEGIN-Blocks nicht bemerkt: Eine nicht zugewiesene Variable wird in einem numerischen Kontext als 0 behandelt, andernfalls als leere Zeichenfolge. Eine nicht zugewiesene Variable ist also falsch inif (p) ...
Glenn Jackman
Ja, mir ist aufgefallen, dass es jedes Mal am BEGIN-Block auf Null gesetzt werden muss, da es als Schalter dient. Aber interessanterweise habe ich es jetzt mit Script versucht $0 ~ pattern, und es funktioniert nicht, aber /'"$1"'/damit funktioniert es !? : O
branquito
Vielleicht hat es etwas mit der Art und Weise zu tun $line, wie Muster abgerufen werden. Die Mustersuche wird an der Ausgabe von durchgeführt whois $line, $linedie aus einer Datei in einem WHILE DO-Block stammt.
Branquito
Bitte zeigen Sie den Inhalt von $line- machen Sie es in Ihrer Frage für die richtige Formatierung.
Glenn Jackman
17
awk  -v pattern="$1" '$0 ~ pattern'

Hat ein Problem darin, dass awkdie ANSI C-Escape-Sequenzen (wie \nfür Newline, \ffür Form Feed, \\für Backslash und so weiter) in erweitert $1. Daher wird es zu einem Problem, wenn $1Zeichen mit umgekehrten Schrägstrichen enthalten sind, wie es in regulären Ausdrücken üblich ist (ab GNU awk4.2 sind Werte, die mit @/und enden /, ebenfalls ein Problem ). Ein anderer Ansatz, der nicht unter diesem Problem leidet, besteht darin, es zu schreiben:

PATTERN=$1 awk '$0 ~ ENVIRON["PATTERN"]'

Wie schlimm es wird, hängt von der awkImplementierung ab.

$ nawk -v 'a=\.' 'BEGIN {print a}'
.
$ mawk -v 'a=\.' 'BEGIN {print a}'
\.
$ gawk -v 'a=\.' 'BEGIN {print a}'
gawk: warning: escape sequence `\.' treated as plain `.'
.
$ gawk5.0.1 -v 'a=@/foo/' BEGIN {print a}'
foo

Alle awks funktionieren jedoch für gültige Escape-Sequenzen gleich:

$ a='\\-\b' awk 'BEGIN {print ENVIRON["a"]}' | od -tc
0000000   \   \   -   \   b  \n
0000006

(Inhalt des übergebenen $aZustandes)

$ awk -v a='\\-\b' 'BEGIN {print a}' | od -tc
0000000   \   -  \b  \n
0000004

( \\geändert in \und \bgeändert in ein Backspace-Zeichen).

Stéphane Chazelas
quelle
Sie sagen also, wenn Muster zum Beispiel \d{3}drei Ziffern finden würde, würde das nicht wie erwartet funktionieren, wenn ich Sie gut verstehe?
Branquito
2
für \ddie es sich nicht um eine gültige C-Escape-Sequenz handelt, hängt dies von Ihrer awkImplementierung ab ( awk -v 'a=\d{3}' 'BEGIN{print a}'zur Überprüfung ausführen ). Aber für \` or \ b , yes definitely. (BTW, I don't know of any awk implementations that understands \ d 'als eine Ziffer).
Stéphane Chazelas
es heißt: awk warning - escape sequence \d' treated as plain d 'd {3}, also hätte ich in diesem Fall wohl ein Problem?
Branquito
1
Tut mir leid, ich hatte einen Tippfehler in meiner Antwort. Der Name der Umgebungsvariablen muss mit ENVIRON["PATTERN"]der PATTERNUmgebungsvariablen übereinstimmen. Wenn Sie eine Shell-Variable verwenden möchten, müssen Sie sie zuerst exportieren ( export variable) oder die ENV=VALUE awk '...ENVIRON["ENV"]'env-var-Übergabesyntax wie in meiner Antwort verwenden.
Stéphane Chazelas
1
Weil Sie eine Shell-Variable exportieren müssen, damit sie in der Umgebung an einen Befehl übergeben wird.
Stéphane Chazelas
5

Versuchen Sie etwas wie:

awk -v l="$line" -v search="$pattern" 'BEGIN {p=0}; { if ( match( $0, search )) {p=1}}; END{ if(p) print l >> "outfile.txt" }'
Hunter Eidson
quelle
Wenn sich dies genauso verhält wie /regex/beim Auffinden von Mustern, könnte dies eine gute Lösung sein. Ich werde es versuchen.
Branquito
1
Die Schnelltests, die ich durchgeführt habe, schienen gleich zu funktionieren, aber ich kann es nicht einmal garantieren ... :)
Hunter Eidson
0

Nein, aber Sie können das Muster einfach in die doppelte Anführungszeichenfolge interpolieren, die Sie an awk übergeben:

awk -v l="$line" "BEGIN {p=0}; /$pattern/ {p=1}; END{ if(p) print l >> \"outfile.txt\" }"

Beachten Sie, dass Sie jetzt das doppelte Anführungszeichen awk-Literal umgehen müssen, dies jedoch immer noch am einfachsten zu bewerkstelligen ist.

Kilian Foth
quelle
Ist diese Methode sicher, wenn $patternLeerzeichen enthalten sind? Mein Beispiel von oben funktioniert, da $ 1 mit doppelten Anführungszeichen "$ 1" geschützt ist, aber nicht sicher ist, was in Ihrem Fall passiert.
Branquito
2
Ihr ursprüngliches Beispiel beendet die Zeichenfolge in einfachen Anführungszeichen an der Sekunde ', schützt dann die $1doppelten Anführungszeichen und greift dann eine weitere Zeichenfolge in einfachen Anführungszeichen für die zweite Hälfte des awk-Programms an. Wenn ich das richtig verstehe, sollte dies genau den gleichen Effekt haben wie das Schützen der $1äußeren einfachen Anführungszeichen - awk sieht niemals die doppelten Anführungszeichen, die Sie um sie setzen.
Kilian Foth
4
Aber wenn $patternenthält ^/ {system("rm -rf /")};, dann sind Sie in großen Schwierigkeiten.
Stéphane Chazelas
Ist das nur der Nachteil dieses Ansatzes, wenn alle in "" eingeschlossen sind?
Branquito
-3

Sie können die Funktion eval verwenden, die in diesem Beispiel die Variable nets auflöst, bevor awk ausgeführt wird.

nets="searchtext"
eval "awk '/"${nets}"/'" file.txt
Noxy
quelle