Verwenden der Bash-Shell-Funktion in AWK

Ist es möglich, die Bash-Funktion in AWK irgendwie zu verwenden?

Beispieldatei (string, int, int , int)

Mike 247808 247809 247810 

Es wird versucht, Werte von dezimal nach hexadezimal zu konvertieren.

Funktion, die entweder in .bashrc oder im Shell-Skript.

$ awk "{print $1 ; d2h($2)}" file awk: calling undefined function d2h input record number 1, file file source line number 1 

Antwort

Versuchen Sie es Verwenden Sie die Funktion system():

awk "{printf("%s ",$1); system("d2h " $2)}" file 

In Ihrem Fall wird system Rufen Sie d2h 247808 auf und hängen Sie dann die Ausgabe dieses Befehls an die printf Ausgabe an:

Mike 3C800 

BEARBEITEN:

Als system wird sh anstelle von bash Ich kann keinen Weg finden, auf .bashrc zuzugreifen. Sie können jedoch weiterhin Funktionen aus Ihrem aktuellen Bash-Skript verwenden:

#!/bin/bash d2h() { # do some cool conversion here echo "$1" # or just output the first parameter } export -f d2h awk "{printf("%s ",$1); system("bash -c "\""d2h "$2""\""")}" file 

BEARBEITEN 2:

Ich weiß es nicht warum, aber das funktioniert nicht auf meinem Ubuntu 16.04. Dies ist seltsam, da es früher unter Ubuntu 14.04 funktioniert hat.

Kommentare

  • Dies könnte funktionieren, wenn d2h war eine ausführbare Datei, aber nicht, wenn ‚ eine “ -Funktion ist, die entweder in .bashrc oder im Shell-Skript .
  • @MichaelHomer Ja, Sie haben Recht. Dank Ihres Kommentars wurde mir klar, dass ich die Frage beantwortet habe, die niemand gestellt hat. Ich habe jedoch einen Weg gefunden, Funktionen aus dem aktuellen Skript (und möglicherweise aus anderen Skripten über source) zu verwenden, aber ich kann ‚ f Abbildung herausfinden, warum es ‚ nicht mit .bashrc funktioniert.
  • Das ‚ s ist auch eine Sicherheitsanfälligkeit bezüglich Befehlsinjektion, da der Inhalt von $2 als Shell-Code interpretiert wird.
  • Wenn export -f d2h funktioniert nicht ‚ funktioniert nicht wie bei ksh / zsh: (\ 42 und \ 47 sind die Escape-Codes für einfache und doppelte Anführungszeichen) CODE=$(typeset -f d2h) awk ' { system("bash -c \47eval \42$CODE\42; d2h \42"$2"\42\47") }'

Antwort

Sie können bash von awk aus aufrufen und dessen Ausgabe verwenden. Das ist aus Sicht der Leistung offensichtlich gefährlich, wenn es zu oft vorkommt. Zitieren der Manpage:

command | getline [var] 

Führen Sie den Befehl aus, der die Ausgabe entweder in $ 0 oder var,

Befehl wäre ein Bash-Skript, das die Funktionsdefinition enthält und die Funktion ausführt.

Antwort

Das Konvertieren von Dezimal in Hexadezimal ist etwas, das awk sehr gut selbst tun kann. Und Sie könnten eine awk -Funktion definieren, um dies zu tun:

 function d2h(d) { return sprintf("%x", d) }  

Um nun die Frage im allgemeinen Fall zu beantworten, muss awk bash -Funktionen benötigen Sie awk, um eine bash -Shell auszuführen, die bash um die Definition dieser Funktion zu interpretieren und diese Funktion aufzurufen, wobei der von awk extrahierte Wert als Argumente übergeben wird.

Nicht trivial.

bash unterstützt das Exportieren von Funktionen über die Umgebung, sodass es in nachfolgenden Aufrufen von bash verfügbar ist, sodass dies eine Möglichkeit zum Übergeben ist die Definition der Funktion für die bash, die von awk aufgerufen wird:

export -f d2h 

Die einzige Möglichkeit für awk, einen Befehl auszuführen (bash h ere) sind mit seiner system("cmd") oder print... | "cmd" oder "cmd" | getline. In allen Fällen führt awk eine Shell aus, um das cmd zu interpretieren. Es wird jedoch sh, nicht bash. Sie müssen also eine Befehlszeile für sh erstellen, die ein bash -Aufruf ist, der einen bash Befehlszeile, um die Funktion aufzurufen, daher müssen Sie beim Zitieren vorsichtig sein:

export -f d2h <file awk -v q=""" " function shquote(s) { gsub(q, q "\\" q q, s) return q s q } {print $1; system("exec bash -c "\""d2h \"$1\""\"" bash " shquote($2))}" 

Wenn Sie die Ausgabe der Funktion wieder in awk, Sie müssten es über eine Pipe zurück übertragen. Dazu würden Sie „cmd | getline anstelle von (wodurch die Standardausgabe von cmd unberührt bleibt).

cmd | getline line speichert eine Zeile (genau genommen einen Datensatz , Datensätze sind standardmäßig Zeilen) Um die gesamte Ausgabe in den Fällen zu erhalten, in denen sie aus mehreren Zeilen besteht, benötigen Sie eine Schleife wie:

 awk "... cmd = "exec bash -c "\""d2h \"$1\""\"" bash " shquote($2) output = "" while ((cmd | getline line) > 0) { output = output line RS } sub(RS "$", "", output) # remove the last newline ..."  

Dies bedeutet, dass für jeden Aufruf der Funktion eine sh und eine bash ausgeführt wird wird also ziemlich ineffizient sein. Dies wäre sogar noch wesentlich ineffizienter, als wenn bash das Lesen und Teilen mit einer while read loop:

(unset -v IFS; while read -r a b rest; do printf "%s\n" "$a" d2h "$b" done < file) 

Beachten Sie außerdem, dass bash seit dem Shellshock jetzt Funktionen in Umgebungsvariablen exportiert, die den Namen BASH_FUNC_d2h%%. Einige sh -Implementierungen, einschließlich mksh und neuerer Versionen von dash remove diese Umgebungsvariablen aus der Umgebung:

$ env "foo%%=bar" dash -c "printenv foo%%" $ env "foo%%=bar" mksh -c "printenv foo%%" $ env "foo%%=bar" zsh -c "printenv foo%%" bar $ env "foo%%=bar" bash -c "printenv foo%%" bar 

Anstatt sich auf die schwache Funktion zum Exportieren von Funktionen zu verlassen, können Sie die Funktionsdefinition auch auf andere Weise übergeben. Dies kann über eine Umgebungsvariable mit einem üblichen Namen erfolgen:

 BASH_FUNCTIONS=$(typeset -f d2h) awk " ... cmd = "exec bash -c "\""eval \"$BASH_FUNCTIONS\";" \ "d2h \"$1\""\"" bash " shquote($2) ..."  

Antwort

Versuchen Sie Folgendes:

awk "{print $1 ; printf "%x\n", $2}" file 

AFAIK, Sie können keine Bash verwenden Funktion in awk, aber nur ein Skript. Bei Bedarf können Sie eine awk-Funktion verwenden.

Antwort

Verwenden einer benutzerdefinierten Bash-Funktion inside awk

Haftungsausschluss: Mir ist klar, dass dies nicht das ist, was das OP versucht, aber Google wird andere wie mich zu dieser Antwort führen.

Situation

Sie haben ein bash -Skript, das mit Funktionen organisiert ist (weil Sie sich selbst oder [die meisten] Mitarbeiter nicht hassen), und mindestens eine dieser Funktionen muss Rufen Sie einen anderen aus awk auf.

Lösung

Skript

#!/bin/env bash # The main function - it"s a sound pattern even in BASH main(){ # In the awk command I do some tricky things with single quotes. Count carefully... # The first $0 is outside the single quotes so it is the name of the current bash script. # The second $0 is inside the single quotes so it is awk"s current line of input. awk "{printf("%s. ", ++c); system(""$0" --do"); print $0}"<<-PRETEND_THIS_IS_AN_INPUT_STREAM and and well PRETEND_THIS_IS_AN_INPUT_STREAM } # functionized to keep things DRY doit(){ echo -n "doin" it " } # check for a command switch and call different functionality if it is found if [[ $# -eq 1 && $1 == "--do" ]]; then doit else main fi 

Ausgabe

$ ./example.sh 1. doin" it and 2. doin" it and 3. doin" it well 

Kommentare

  • ich Sie repräsentieren Queens und sind in Brooklyn aufgewachsen.

Antwort

Nur ein kurzes Beispiel, um @HaukeLaging zu demonstrieren command|getline:

  1. Eingabe sei:
Dear friends my name is `id -nu` and today is `date "+%Y-%m-%d"`. 

Wenn wir der Shell-Syntax folgen, wird in der Eingabe

`command` 

verwendet, um Inline-Befehle zu bezeichnen, die durch das Ergebnis von ersetzt werden sollen seine Ausführung.

  1. Wir können den Inline-Shell-Befehl erweitern durch:
#!/usr/bin/gawk -f BEGIN { FS ="`"; } NF==3 { $2 | getline $2 } { print } 
  1. Verwendung (nach dem üblichen chmod):
$ expand-inline input Dear friends my name is jjoao and today is 2018-01-15. 

Antwort

Dies gibt Ihnen gute Chancen.

cat ../logs/em2.log.1 |grep -i 192.168.21.15 |awk "{system("date"); print $1}" 

Mit der Systemfunktion können Sie den Befehl bash innerhalb eines awk-Streams analysieren.

Antwort

Diese Antwort gilt für GNU awk, aber andere Versionen implementieren möglicherweise denselben Trick. Die Idee ist, dass, solange die rechte Seite des Rohrs genau gleich ist und nicht austritt, awk das Rohr nicht schließt. Wenn es anders ist, wird ein neues Rohr geöffnet. Wenn Sie beispielsweise Folgendes eingeben:

awk "BEGIN{ shell= "/bin/bash" } { print $0 | "/bin/bash" } END { print "echo $a" | " /bin/bash" }" 

können Sie Folgendes eingeben:

a=5 echo $a 

und Erhalten Sie die erwartete (?) Antwort 5: Es wurde keine neue Shell erzeugt. Aber im Abschnitt END habe ich ein zusätzliches Leerzeichen eingegeben, sodass die RHS der Pipe unterschiedlich ist und eine neue Shell ohne Wert für die Variable a erzeugt wird (und daher beim Beenden eine leere Zeile druckt. Ich kann sie nicht in der finden Dokumentation, aber die empfohlene Methode zur Verwendung von Langzeit-Pipes ist wie folgt: Fügen Sie den Befehl im Abschnitt BEGIN in eine Variable ein und verwenden Sie diese Variable erneut.

Geben Sie einer Bash-Sitzung und eine benutzerdefinierte Funktion dann ist die Wiederverwendung eine Übung, die dem Leser überlassen bleibt 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.