Ich habe ein Problem mit dem folgenden (G) AWK-Skript:
do {
...
} while (system("sleep 10"))
Meine Absicht ist es, die Schleife zu unterbrechen, wenn der Benutzer im Ruhezustand ^ C drückt, aber es funktioniert nicht.
Ich glaube, das Problem ist, dass Bash mit 0 beendet wird, wenn es mit ^ C unterbrochen wird, zumindest wenn es von AWKs ausgeführt wird system()
:
$ awk 'BEGIN { print "\n" system("sleep 2") }'
(let the sleep complete)
0
$ awk 'BEGIN { print "\n" system("sleep 2") }'
^C
0
Wieso ist es so?
Ist das ein Fehler in Bash oder in (G) AWK?
Gibt es eine einfache Lösung, die keine komplizierte Bash-spezifische Syntax beinhaltet trap
?
Das Beste, was ich mir einfallen lassen kann, ist Folgendes:
do {
...
} while (42 == system("sleep 10 && exit 42"))
das sieht für mich immer noch wie ein Kludge aus.
Antworten:
Was awk's zurückgeben
system()
sollte, ist schlecht spezifiziert .Was bei
awk
Implementierungen üblich zu sein scheint, ist, dass bei einem normalen Exit der Exit-Code (die anexit(3)
Modulo 256 übergebene Nummer ) zurückgegeben wird. Wenn der Shell-Prozess jedoch durch ein Signal beendet wird, gibt es viele verschiedene Verhaltensweisen.Beachten Sie auch, dass die C-Funktion
system(3)
zwar SIGINT (und SIGQUIT) im übergeordneten Element ignorieren soll, es jedoch (zumindest für mich) nicht sehr klar ist, dass die Anforderung auch fürawk
's giltsystem()
. Einigeawk
Implementierungen (wiemawk
) sterben an diesem SIGINT (das ist auch das Verhalten, das ich gerne sehen würde, da ich nicht mag, dass meine STRG-C ignoriert wird, nur weilawk
zufällig diesystem()
Funktion ausgeführt wird), andere (wiegawk
oder traditionelle Implementierungen) Gewohnheit.Beachten Sie auch, dass einige Shells einige dieser Signale abfangen und schließlich aufrufen können,
exit()
was sich auf das Verhalten auswirken würde (siehe beispielsweise die Diskussion in den Kommentaren zur Bourne-Shell). Deshalb verwende ichexec
in den folgenden Beispielen, um die Shell aus der Schleife zu entfernen.Für den Wert, der von einem SIGINT zurückgegeben wird
system()
(es gibt noch mehr Variationen, wenn Sieclose()
1 berücksichtigen ), sehen wir:0.0078125
Sein2 / 256
(für einenSEGV
von11
würde man 0,542969 ((128 + 11) / 256) erhalten, wenn ein Kern entleert wurde, andernfalls 0,0429688 (11/256)),nawk
dasnawk
auf Solaris 10 oder 11 gefundene oder sein Linux-Port im Erbstück Werkzeugkasten,bwk-awk
wobei dieawk
gepflegt von Brian Kernighan selbst (dieK
inawk
) die Grundlage für dieawk
auf einigen BSDs gefunden (hier getestet auf Debian GNU / Linux)./usr/xpg4/bin/awk
unter Solaris 11 verhält sich wiegawk
.So basierend auf dem Wert
s
von zurückgegebensystem(3)
(eine Ganzzahl , wo die Bits 0 bis 6 ist die Signalnummer sind, Bit 7 das Kern-Bit, und Bit 8 bis 15 der Beendigungscode),awk
‚ssystem()
obigen gibt entweder:s / 256
(traditionelleawk
Implementierungen),int(s/256)
(gawk
),mawk
, die gleiche Transformation, wie sie beispielsweise von Shells wie der Bourne- oder C-Shell durchgeführt wird ((s&127)+128
wenn sie getötet werden,s>>8
andernfalls), außer dass, wenn ein Core entleert wird, Sie(s&127)+256
stattdessen(s&127)+128
(der Wert ist(s&255)+128
) erhalten.Hier könnten Sie stattdessen Folgendes tun:
Aber es würde immer noch dazu führen
awk
, mit einigenawk
Implementierungen wie getötet zu werdenmawk
. Wenn Ihrsh
istbash
oderyash
, könnten Sie tun:Wird
sleep
also in einer eigenen Prozessgruppe ausgeführt (und nur es erhält das SIGINT).Eine andere Alternative könnte darin bestehen, SIGINT vor dem Aufruf zu ignorieren
awk
. Die meisten Shells (und das ist eine POSIX-Anforderung) können einen Signalhandler jedoch nicht ändern, wenn das Signal beim Start bereits ignoriert wurde. Also Dinge wie:Wird nicht funktionieren.
zsh
hat diese (selbstverschuldete) Einschränkung jedoch nicht. Wenn Sie also wissen, dass siezsh
verfügbar ist, können Sie Folgendes tun:Welche funktionieren würde , ob
awk
istmawk
,gawk
oder eine andere und würde vermieden , mit Auftragssteuerung zu Chaos mit. An dieser Stelle lohnt es sich jedoch, die Verwendung vonperl
/python
/ruby
... in Betracht zu ziehen, anstattawk
die Signalverarbeitung an Ihre Bedürfnisse anzupassen.Anmerkungen
1 Nach
close()
einer Pipeline wie in:Erstens
^C
unterbricht dieses Malawk
alle Implementierungen, die ich versucht habe (es gibt keine solche Anforderung, SIGINT / SIGQUIT fürpopen(3)
/ zu ignorierenpclose(3)
(ein natürlicher Weg, dies zu implementierengetline
), wie es für istsystem(3)
).Aber wenn es um den Exit-Status geht (wo
s
ist der vonpclose(3)
/waitpid(2)
like fürsystem()
oben zurückgegebene Wert ), sehen wir:nawk
: funktioniert nicht, so kann manclose()
in Solaris nicht anrufennawk
./usr/xpg4/bin/awk
unter Solaris. Gibt immer 0 zurück, auch wennexit(1)
der Prozess abgeschlossen ist. Offensichtlich ein Konformitätsfehler.gawk
undbwk-awk
: gibts
(exit 1
gibt 256, getötet von SIGINT ergibt 2, getötet von einem SIGSEGV von 11 mit Kern ergibt 139).mawk
: wie fürsystem()
, sieht aus wiemawk
ist die einzige Implementierung, die darüber nachgedacht hat.quelle
set -m
an sich scheint der Trick zu tun!system("set -m; sleep 10")
ist tatsächlich die sauberste Lösung der Reihe und funktioniert zuverlässig über alle AWKs hinweg. Genial!yash
. Beachten Sie, dass die Verwendung vonset -m
nicht portabel ist (funktioniert nicht mit anderensh
mir bekannten Implementierungen ). Und es verhindert, dass Sieawk
im Hintergrund laufen ./bin/sh
in vielen Systemen die Standardeinstellung ist :-(nawk
sondern etwas anderes.