Ich habe ein Shell-Skript , das von der Standardeingabe liest . In seltenen Fällen ist niemand bereit, Eingaben zu machen, und das Skript muss eine Zeitüberschreitung aufweisen . Im Falle einer Zeitüberschreitung muss das Skript einen Bereinigungscode ausführen. Wie geht das am besten?
Dieses Skript muss sehr portabel sein , einschließlich für Unix-Systeme des 20. Jahrhunderts ohne C-Compiler und für eingebettete Geräte, auf denen busybox ausgeführt wird. Perl, bash, jede kompilierte Sprache und sogar das vollständige POSIX.2 können daher nicht verwendet werden. Insbesondere $PPID
, read -t
und perfekt POSIX-kompatibele Fallen sind nicht verfügbar. Das Schreiben in eine temporäre Datei ist ebenfalls ausgeschlossen. Das Skript wird möglicherweise auch dann ausgeführt, wenn alle Dateisysteme schreibgeschützt bereitgestellt sind.
Um die Sache noch schwieriger zu machen, möchte ich auch, dass das Skript einigermaßen schnell ist, wenn keine Zeitüberschreitung auftritt. Insbesondere verwende ich das Skript auch in Windows (hauptsächlich in Cygwin), wo Fork und Exec besonders niedrig sind, so dass ich ihre Verwendung auf ein Minimum beschränken möchte.
Kurz gesagt, ich habe
trap cleanup 1 2 3 15
foo=`cat`
und ich möchte eine Auszeit hinzufügen. Ich kann nicht cat
mit dem read
eingebauten ersetzen . Im Falle eines Timeouts möchte ich die cleanup
Funktion ausführen .
Hintergrund: Dieses Skript errät die Codierung des Terminals, indem es einige 8-Bit-Zeichen druckt und die Cursorposition davor und danach vergleicht. Der Anfang des Skripts testet, dass stdout mit einem unterstützten Terminal verbunden ist, aber manchmal lügt die Umgebung (z. B. plink
setzt, TERM=xterm
auch wenn es mit aufgerufen wirdTERM=dumb
). Der relevante Teil des Skripts sieht folgendermaßen aus:
text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code
stty eol 0 eof n -echo # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position
initial_report=`tr -dc \;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc \;0123456789`
cleanup
# Compute and return initial_x - final_x
Wie kann ich das Skript so ändern, dass tr
es beendet wird und das Skript die cleanup
Funktion ausführt , wenn nach 2 Sekunden keine Eingabe gelesen wurde ?
Antworten:
Was ist damit:
Das heißt: Führen Sie den Befehl zur Ausgabeerzeugung aus und
sleep
in derselben Prozessgruppe eine Prozessgruppe, die nur für sie bestimmt ist. Welcher Befehl zuerst zurückgibt, beendet die gesamte Prozessgruppe.Würde sich jemand fragen: Ja, die Pfeife wird nicht benutzt; Es wird mit den Umleitungen umgangen. Der einzige Zweck besteht darin, dass die Shell die beiden Prozesse in derselben Prozessgruppe ausführt.
Wie Gilles in seinem Kommentar ausführte, funktioniert dies in einem Shell-Skript nicht, da der Skriptprozess zusammen mit den beiden Unterprozessen abgebrochen würde.
Eine Möglichkeit¹, die Ausführung eines Befehls in einer separaten Prozessgruppe zu erzwingen, besteht darin, eine neue interaktive Shell zu starten:
Dies kann jedoch zu Einschränkungen führen (z. B. wenn stdin kein tty ist?). Die stderr-Umleitung dient dazu, die Meldung "Abgebrochen" zu entfernen, wenn die interaktive Shell beendet wird.
Getestet mit
zsh
,bash
unddash
. Aber was ist mit Oldies?B98 schlägt die folgende Änderung vor, die unter Mac OS X mit GNU Bash 3.2.57 oder Linux mit Bindestrich funktioniert:
-
1. andere als
setsid
die, die nicht dem Standard entsprechen.quelle
kill 0
beendet auch den Aufrufer des Skripts. Gibt es eine tragbare Möglichkeit, die Pipeline in eine eigene Prozessgruppe zu zwingen?setprgp()
ohne findensetsid
:-(Sie haben bereits 15 abgefangen (die numerische Version von
SIGTERM
, die,kill
sofern nicht anders angegeben, gesendet wird). Sie sollten also bereits bereit sein, loszulegen. Beachten Sie jedoch, dass die Shell-Funktionen möglicherweise nicht vorhanden sind (sie stammen aus der Shell von System V).quelle
cat
das Beenden warten . Ich habe bereits ein wenig experimentiert, aber noch nichts gefunden, mit dem ich zufrieden bin.$!
, ich denke, einige der Maschinen, die ich verwende, haben keine Jobkontrolle und sind$!
universell verfügbar?bash
spezifische Dinge, die ich einmal mit Missbrauch getan habe-o monitor
. Als ich das schrieb, dachte ich an wirklich alte Muscheln (es funktionierte in Version 7). Das heißt, ich denke, Sie können eines von zwei anderen Dingen tun: (1) Hintergrund "was auch immer" undwait $!
(2) auch sendenSIGCLD
/SIGCHLD
... aber auf alt genug Maschinen ist die letztere entweder nicht vorhanden oder nicht portierbar (die erste ist System III / V, letztere BSD und V7 hatten keine).$!
zumindest auf V7 zurück und stammt sicherlich von einemsh
Vorgänger, der etwas über Jobkontrolle wusste (tatsächlich hat/bin/sh
BSD lange Zeit keine Jobkontrolle durchgeführt; man musste rennencsh
, um es zu bekommen - war aber$!
da ).$!
.Obwohl coretuils ab Version 7.0 einen Timeout-Befehl enthält, haben Sie einige Umgebungen erwähnt, in denen dies nicht möglich ist. Zum Glück hat pixelbeat.org ein Timeout-Skript geschrieben
sh
.Ich habe es schon mehrmals benutzt und es funktioniert sehr gut.
http://www.pixelbeat.org/scripts/timeout ( Hinweis: Das folgende Skript wurde gegenüber dem auf pixelbeat.org geringfügig geändert. Siehe die Kommentare unter dieser Antwort.)
quelle
</dev/stdin
ist ein No-Op.</dev/tty
würde es ermöglichen, vom Terminal zu lesen, was für meinen Anwendungsfall gut genug ist.Was ist mit (ab) NC dafür?
Mögen;
Oder in einer einzigen Befehlszeile zusammengefasst;
Die letzte Version, obwohl sie seltsam aussieht, funktioniert tatsächlich, wenn ich sie auf einem Linux-System teste. Ich vermute, dass sie auf jedem System funktionieren sollte, und wenn nicht, kann eine Variation der Ausgabeumleitung die Portabilität beeinträchtigen. Vorteil hierbei ist, dass keine Hintergrundprozesse involviert sind.
quelle
nc
auf einigen alten Unix-Boxen noch auf vielen Embedded-Linux.Eine andere Möglichkeit, die Pipeline in einer eigenen Prozessgruppe auszuführen, besteht darin, sie
sh -c '....'
mit demscript
Befehl (der diesetsid
Funktion implizit anwendet ) in einem Pseudoterminal auszuführen .quelle
script
auf einigen alten Unix-Boxen noch auf vielen Embedded-Linux.Die Antwort unter https://unix.stackexchange.com/a/18711 ist sehr nett.
Ich wollte ein ähnliches Ergebnis erzielen, aber ohne die Shell erneut explizit aufrufen zu müssen, weil ich vorhandene Shell-Funktionen aufrufen wollte.
Mit bash ist Folgendes möglich:
Angenommen, ich habe bereits eine Shell-Funktion
f
:Wenn ich das ausführe, sehe ich:
quelle
quelle