Vor einigen Wochen habe ich eine seltsame Antwort auf die Frage " (Wie) starte Aufgabe (n) stillschweigend im Hintergrund? " Gesehen . Diese Lösung scheint falsch zu sein ( siehe meine Antwort ), obwohl die Shell die Aufgabe im Hintergrund still zu starten scheint.
I. Problem: Können wir den Shell-Standardfehler tatsächlich umleiten?
Es gibt keine Erklärung für die vorgeschlagene Lösung und eine Analyse liefert keine verlässliche Antwort.
Unten sehen Sie das Code-Snippet.
# Run the command given by "$@" in the background
silent_background() {
if [[ -n $BASH_VERSION ]]; then
{ 2>&3 "$@"& } 3>&2 2>/dev/null
fi
}
Der problematische Befehl ist { 2>&3 "$@"& } 3>&2 2>/dev/null
.
i) Analysieren
Die Befehlsgruppe ( { ... }
) gibt zwei Umleitungen ( 3>&2
und 2>/dev/null
) für einen Befehl ( # Run the command given by "$@" in the background
) an. Der Befehl ist eine asynchrone Liste ( cmd&
) mit einer Umleitung ( 2>&3
).
POSIX-Spezifikation
Jede Umleitung gilt für alle Befehle innerhalb des zusammengesetzten Befehls, die diese Umleitung nicht explizit überschreiben.
Die Standardfehlerumleitung 2>/dev/null
wird durch die der asynchronen Liste zugeordnete Umleitung überschrieben 2>&3
.
Konkrete Fälle
Im ersten Fall wird der Standardfehler umgeleitet, /dev/null
während im zweiten Fall der Standardfehler des Befehls weiterhin an das Terminal angehängt wird.
prompt% { grep warning system.log& } 2>/dev/null
prompt% { 2>&3 grep warning system.log& } 3>&2 2>/dev/null
grep: system.log: No such file or directory
Unten sehen wir ähnliche Fälle. Im ersten Fall ist die Standardausgabe des Befehls weiterhin an das Terminal angehängt. Im zweiten Fall wird die Standardausgabeumleitung des Befehls geändert: >echo.txt
wird von überschrieben >print.txt
.
prompt% { >&3 echo some data...& } 3>&1 >echo.txt
[1] 3842
some data...
prompt% file echo.txt
echo.txt: empty
prompt% { >&3 echo some data...& } 3>print.txt >echo.txt
[1] 2765
prompt% file echo.txt
echo.txt: empty
prompt% cat print.txt
some data...
ii) Beobachtungen
Wie bereits erwähnt, scheint der Befehl im Hintergrund lautlos zu starten. Genauer gesagt wird die Benachrichtigung über einen Hintergrundjob, z. B. [1] 3842
, nicht angezeigt.
Die vorherige Analyse impliziert, dass die Weiterleitungen überflüssig sind, da sie möglicherweise abgebrochen werden.
{ redir_3 cmd& } redir_1 redir_2
ist äquivalent zu cmd&
.
iii) Interpretation
In meinen Augen verbirgt das erwähnte Konstrukt die Benachrichtigung aufgrund einer Nebenwirkung.
Können Sie erklären, wie das passiert?
II. Hypothese (s)
Ilkkachus Antwort und Kommentare ermöglichten einige Fortschritte. Beachten Sie, dass jeder Prozess seine eigenen Dateideskriptoren hat.
- Die Shell-Nachrichten können über den Shell-Standardfehler gesendet werden.
Ein weiterer Kontext: Eine asynchrone Liste, die in einer Subshell ausgeführt wird. Der Befehl g
existiert nicht.
prompt% ( g& )
g: command not found
Asynchrone Befehle, Befehle in Klammern, ..., werden in einer Subshell-Umgebung ausgeführt, die ein Duplikat der Shell-Umgebung ist ...
Eine Subshell erbt den Wert ihres Standardfehlerstroms von ihrer übergeordneten Shell.
Daher sollten sich im vorherigen Fall ihre Standardfehlerströme auf das Terminal beziehen. Die Jobbenachrichtigung wird jedoch nicht angezeigt, während die Shell-Fehlermeldung wahrscheinlich im Shell-Standardfehler angezeigt wird.
( ( g & ) )
Beispiel von dem oben angeforderten unterscheidet, da in Klammern eine Unterschale eingerichtet ist und keine Unterschale vorhanden ist { 2>&3 "$@"& } 3>&2 2>/dev/null
.
()
startet immer noch eine Unterschale, auch wenn es nur ein Paar gibt. Die Zahnspangen {}
nicht. Soweit ich fork()
weiß, ist Bash immer beim Starten einer Subshell, und das macht Hintergrundprozesse, die von Subshells gestartet werden, etwas anders, da sie keine untergeordneten Elemente des Haupt-Shell-Prozesses sind. Sie werden auch nicht zB in der Ausgabe jobs
von der Haupt-Shell usw. angezeigt (der Punkt der Frage, auf die Sie verlinken). Anscheinend macht sich Bash auch in diesem Fall nicht die Mühe, die Job-ID-Zeile auszudrucken. Das ist eine ganz andere Situation als beim { foo & }
Umleitungstanz.
{ 2>&3 "$@" & } 3>&2 2>/dev/null
ist, die Job-ID-Ausgabezeile loszuwerden, die die Subshell ( "$@" & )
überhaupt nicht druckt ...
print [n] nnnnn
zum Terminal ist. Wobei $ n $ und $ nnnnn $ Zahlen sind.