Wie drucke ich meinen eigenen Skriptnamen in mawk?

13

In bash steht $0der Name des Skripts, aber in awk, wenn ich ein Skript namens myscript.awk mit folgendem Inhalt erstelle:

#!/usr/bin/awk -f
BEGIN{ print ARGV[0] }

und starte es, es wird nur "awk" ausgegeben. Außerdem wird ARGV [i] mit i> 0 nur für Skriptargumente in der Befehlszeile verwendet. Wie kann man also den Namen des Skripts drucken lassen, in diesem Fall "myscript.awk"?

cipper
quelle
Ich habe den Titel von awk in mawk geändert, da alle Lösungen gawk erfordern und nicht mit general awk funktionieren, insbesondere mit mawk, das weit verbreitet ist (z. B. Standard bei Ubuntu)
cipper
Was lässt Sie denken, dass mawkUbuntu standardmäßig ist? Auf meinem 15.04 VM, die Standardeinstellung awkist gawk. Während mawk installiert ist, ist dies nicht die Standardeinstellung.
Terdon
1
Es ist ein awk-Skript, wenn Sie es mit aufrufen awk -f myscript.awk. Dies hat jedoch nichts mit dem fraglichen Problem zu tun.
Cipper
1
@EdMorton Es ist ein awkSkript, weil es mit beginnt #!/usr/bin/awk -f. Shell-Skripte beginnen mit #!/bin/sh(oder ähnlichem).
Barmar
1
Ich habe mit verschiedenen Shell-Experten gesprochen und versucht, eine endgültige Antwort darauf zu bekommen, ob es sich um ein Shell- oder ein awk-Skript handelt, und laut POSIX überraschenderweise die Interpretation von Dateien, die mit # beginnen! ist undefiniert und hat keinen bestimmten Typnamen. Während einige Leute es eher als "Hash-Bang-Interpreter-Skript" als als Shell- oder Awk-Skript bezeichnen, scheint der Konsens zu bestehen, dass es als Awk-Skript betrachtet werden sollte, obwohl der Kernel (nicht die Shell) die erste Zeile interpretiert, weil Awk immer noch muss auch diese erste Zeile (als Kommentar) parsen können und kann mit ausgeführt werden awk -f file.
Ed Morton

Antworten:

5

Mit GNU awk 4.1.3 in Bash auf Cygwin:

$ cat tst.sh
#!/bin/awk -f
BEGIN { print "Executing:", ENVIRON["_"] }

$ ./tst.sh
Executing: ./tst.sh

Ich weiß nicht, wie portabel das ist. Wie immer würde ich ein awk-Skript nicht mit einem shebang in einem Shell-Skript ausführen, da es Sie nur der möglichen Funktionalität beraubt. Halte es einfach und tue dies stattdessen:

$ cat tst2.sh
awk -v cmd="$0" '
BEGIN { print "Executing:", cmd }
' "$@"

$ ./tst2.sh
Executing: ./tst2.sh

Letzteres funktioniert mit jedem modernen awk in jeder Shell auf jeder Plattform.

Ed Morton
quelle
Beachten Sie, dass der erste nur in bash, zsh oder ksh funktioniert. Das letztere handelt von Shell-Skripten, nicht von awk-Skripten.
Cuonglm
2
Vielen Dank! ENVIRON["_"]funktioniert einwandfrei und ruft kein externes Programm auf. Die zweite Option awk -v ...hängt davon ab, wie das Skript ausgeführt wird. Ich will das nicht.
Cipper
1
Das Aufrufen Ihres Skripts tst.shist irreführend. Es ist ein awkSkript, kein Shell-Skript. BEGINist kein gültiger Shell-Befehl.
Barmar
1
Richtig, aber die Frage zur Portabilität lautet nicht "Ist ENVIRON [] portabel?", Sondern "Erzeugt ENVIRON["_"]den Pfad des aufrufenden Shell-Skripts, wenn er von jeder awk gedruckt wird, die über einen shebang von jeder Shell aufgerufen wird." Ich würde niemals ein awk-Skript von einem shebang anrufen, mir persönlich ist die Antwort egal, aber ich dachte nur, ich würde es erwähnen ... Oh, ich sehe in den obigen Kommentaren, dass @cuonglm antwortete, dass es nur in einigen Shells unterstützt wird .
Ed Morton
1
Guter Punkt, @Ed. Bestätigt als fehlgeschlagen im Bindestrich (der den vorherigen Befehl (oder die Shell selbst) und nicht den aktuellen zurückgibt ). Interessanterweise stellt ksh93 die PID in Sternchen, z *12345*/tmp/test.awk. ARGV[0]ist zuverlässig immer awkin dash, bash, zsh und ksh93.
Adam Katz
5

Ich denke nicht, dass dies gemäß der gawk Dokumentation möglich ist :

Schließlich ARGV[0]variiert der Wert von (siehe Abschnitt 7.5, Integrierte Variablen) je nach Betriebssystem. Einige Systeme geben awkdort den vollständigen Pfadnamen von awk an (z. B. /bin/awk), andere den Namen Ihres Skripts ("advice"). Verlassen Sie sich nicht auf den Wert von ARGV[0], um Ihren Skriptnamen anzugeben.

Auf können linuxSie versuchen, eine Art schmutzigen Hack zu verwenden, und wie in Kommentaren von Stéphane Chazelas gezeigt , ist es möglich, wenn die Implementierung von awkNUL-Bytes unterstützt:

#!/usr/bin/awk -f

BEGIN { getline t < "/proc/self/cmdline"; split(t, a, "\0"); print a[3]; }
Taliezin
quelle
Dein Skript scheint so wie es ist nicht zu funktionieren. Es gibt nur "k" aus, wenn es mit "awk -f script.awk" aufgerufen wird, und es gibt "s" aus, wenn es mit "./script.awk" aufgerufen wird
cipper
@cipper: Hier funktioniert es mit gawkund fällt (wie deine Beschreibung) mit aus mawk. Interessant!
Es funktioniert bei mir unter Linux, awk- 4.0.2. In freebsd mit /proc/curpoc/cmdline, und awkErgebnis ist wie bei Ihnen, funktioniert aber mit gawk.
Taliezin
Auf Standard Ubuntu funktioniert es nicht. Es wäre schön, eine tragbare Lösung zu finden.
Cipper
1
@taliezin: Die Antwort von cuonglm ist keine Lösung, da das Skript mit seinem Namen manuell eingegeben werden muss. Es ist so, awk -vNAME="myscript.awk" ./myscript.awkals würde man NAME im Skript aufrufen und dann ausdrucken. Keine Lösung.
Cipper
5

Ich kenne keine direkte Möglichkeit, den Befehlsnamen aus awk heraus zu ermitteln. Sie können es jedoch über eine Unterschale finden.

gaffen

Mit GNU awk und dem psBefehl können Sie die Prozess-ID von verwenden PROCINFO["PID"], um den Befehlsnamen als Workaround abzurufen. Beispielsweise:

cmdname.awk

#!/usr/bin/gawk -f

BEGIN {
  ("ps -p " PROCINFO["pid"] " -o comm=") | getline CMDNAME
  print CMDNAME
}

mawk und nawk

Sie können den gleichen Ansatz verwenden, jedoch die awkPID aus der $PPIDspeziellen Shell-Variablen (PID des übergeordneten Elements) ableiten :

cmdname.awk

#!/usr/bin/mawk -f

BEGIN { 
  ("ps -p $PPID -o comm=") | getline CMDNAME
  print CMDNAME
}

Testen

Führen Sie das Skript folgendermaßen aus:

./cmdname.awk

Ausgabe in beiden Fällen:

cmdname.awk
Thor
quelle
Ich habe eine Fehlermeldung erhalten: / bin / sh: 1: -o: nicht gefunden
cipper
@cipper: Das funktioniert nur mit GNU awk, ich habe die fehlende Shebang-Zeile hinzugefügt.
Thor
Aus dem Handbuch von gawk : Laut POSIX 'expression | getline ist mehrdeutig, wenn expression andere nicht in Klammern gesetzte Operatoren als '$' enthält, z. B. '"echo" "date" | getline 'ist nicht eindeutig, da der Verkettungsoperator nicht in Klammern steht. Sie sollten es als '("echo" "date") | schreiben getline ', wenn Ihr Programm auf alle awk-Implementierungen portierbar sein soll.
Cipper
1
Wenn es nötig ist gawk, ist es eine gawkLösung anstelle einer awkLösung. Ich denke, @cipper sollte seinen Wunsch "eine tragbare Lösung" zur Frage hinzufügen.
1
@Thor: Die Antwort von cuonglm ist keine Lösung, da das Skript mit seinem Namen manuell eingegeben werden muss. Es ist so, awk -vNAME="myscript.awk" ./myscript.awkals würde man NAME im Skript aufrufen und dann ausdrucken. Keine Lösung.
Cipper
4

Mit POSIX awk:

#!/usr/bin/awk -f

BEGIN {
    print ENVIRON["AWKSCRIPT"]
}

Dann:

AWKSCRIPT=test.awk ./test.awk
test.awk
cuonglm
quelle
4
Sie geben den Namen des Skripts manuell ein, dies ist kein Selbstausdruck
Cipper
@cipper: Nun, das ist der einfachste und tragbarste Weg, den ich mir vorstellen kann.
cuonglm
2
Es ist wie das Aufrufen awk -vNAME="myscript.awk" ./myscript.awkund Drucken der Variablen NAMEim Skript. Keine Lösung.
Cipper
@cipper: Das ist der einzige Weg, wenn Sie erwähnen mawk. Und auch using ENVIRONist nicht dasselbe wie using -vNAME="myscript.awk", da mawkes die Escape-Sequenz in erweitert NAME.
Dienstag,
4

GNU awk verwenden

Überprüfen des GNU awk-Benutzerhandbuchs - 7.5.2 Eingebaute Variablen, auf die ich gestoßen bin:

PROCINFO #

Die Elemente dieses Arrays bieten Zugriff auf Informationen zum laufenden awk-Programm. Die folgenden Elemente (alphabetisch aufgelistet) sind garantiert verfügbar:

PROCINFO ["pid"]

Die Prozess-ID des aktuellen Prozesses.

Dies bedeutet, dass Sie die PID des Programms zur Laufzeit kennen. Dann ist es eine Frage der Verwendung system(), nach dem Prozess mit dieser gegebenen PID zu suchen:

#!/usr/bin/gawk -f
BEGIN{ pid=PROCINFO["pid"]
       system("ps -ef | awk '$2==" pid " {print $NF}'")
}

Ich benutze ps -ef, was die PID in der 2. Spalte anzeigt. Vorausgesetzt, die Ausführung erfolgt über awk -f <script>und keine anderen Parameter, können wir annehmen, dass das letzte Feld der Zeile die gewünschten Informationen enthält.

Wenn wir einige Parameter hätten, müssten wir die Zeile anders analysieren - oder besser einige der Optionen verwenden ps, um nur die Spalten zu drucken, die uns interessieren.

Prüfung

$ awk -f a.awk 
a.awk
$ cp a.awk hello.awk
$ awk -f hello.awk 
hello.awk

Beachten Sie auch, dass uns ein anderes Kapitel des GNU awk-Benutzerhandbuchs sagt, dass ARGV nicht der richtige Weg ist:

1.1.4 Ausführbare awk-Programme

Schließlich variiert der Wert von ARGV [0] (siehe Integrierte Variablen) abhängig von Ihrem Betriebssystem. Einige Systeme geben dort 'awk' an, andere den vollständigen Pfadnamen von awk (z. B. / bin / awk) und andere den Namen Ihres Skripts ('advice'). (dc) Verlassen Sie sich nicht auf den Wert von ARGV [0], um Ihren Skriptnamen anzugeben.

fedorqui
quelle
Leider ist PROCINFO nur eine Gawk-Funktion, keine allgemeine Awk. Zum Beispiel ist es nicht verfügbar in mawk (das standardmäßig in Ubuntu installiert ist)
Cipper
Ich weiß ... Warum hast du die Frage dann mit [gawk] markiert?
Fedorqui
Du hast recht. Bei der Beantwortung der Frage war mir nicht bewusst, was diese Unterschiede zwischen mawk und gawk sind. Das Tag wurde in mawk geändert.
Cipper
@cipper gut :) Ich habe tatsächlich mit getestet mawkund konnte es nicht zum Laufen bringen, so dass ich es gawkin meinem Ubuntu installiert habe und es funktionierte. Ein Workaround kann also sein gawk: D
fedorqui
1
@terdon gawkist unter Ubuntu nicht standardmäßig installiert (oder zumindest in einigen Ubuntu-Versionen, in denen mawkdie Standardimplementierung verwendet wird awk). IIRC, ich musste es auch auf Debian installieren.
Stéphane Chazelas