Warum wird die Funktion erst wieder ausgeführt, wenn der Hintergrundprozess beendet ist?

21

Betrachten Sie dieses Skript:

#!/bin/bash
function start {
  leafpad &
  echo $!
}
PID=$(start)
echo "PID is $PID"

Das Skript wird erst fortgesetzt, wenn der Leafpad-Prozess beendet ist, obwohl es sich um einen Hintergrundprozess handelt.

Warum ist das? Ist es möglich, einen Hintergrundprozess von einer Funktion aus zu starten?

user234565
quelle

Antworten:

22

Die Funktion kehrt zurück, aber die Befehlsersetzung wird blockiert, da Sie einen Hintergrundjob erstellt haben, Ihr stdout fd aber immer noch geöffnet ist. Schließen Sie es einfach durch Hinzufügen >/dev/nullvor dem &.

#!/bin/bash
function start {
  leafpad >/dev/null &
  echo $!
}
PID=$(start)
echo "PID is $PID"

Wenn Ihr Prozess auch stdin, stdout, stderr closed haben soll, verwenden Sie Folgendes:

leafpad >/dev/null 0>&1 2>&1 &

Dadurch werden stdin (0), stdout (1) und stderr (2) und dann background (&) geschlossen. Wenn Sie diese Stream-Umleitungen verwenden, vergessen Sie nicht, dass sie "getäuscht" sind, dh in der Reihenfolge der Ausführung dupliziert.

1>/dev/null 2>&1

und

2>&1 1>/dev/null

sind nicht das Gleiche ! Im ersten Fall duplizieren Sie einen Stream nach / dev / null (was Sie wollen), im zweiten Fall duplizieren Sie / dev / stdout nach stderr und schließen dann stdout. Daher wird jede Nachricht, an die gesendet stderrwird, in Ihrer Konsole angezeigt.

Adrien M.
quelle
Auf meinem System bestätigt
user120161
10
Sie schließen die Streams nicht, sondern leiten sie um.
dcat
4
schließen; n>&-Wo nist der Dateideskriptor?
dcat
1
@dcat: Ja, aber die Umleitung zu / von /dev/nullführt nicht zu E / A-Fehlern, wenn ein Prozess versucht, seine Standardausgabe zu schreiben, aber feststellt, dass dies 1eine ungültige FD ist. Die Terminologie im Beitrag ist also falsch, nicht die eigentliche Bash-Programmierung. (Tatsächlich bedeutet das Duplizieren von FD 1 auf 0, dass stdin ein Dateideskriptor ist, mit O_RDONLYdem geöffnet wird , und der wahrscheinlich einen Fehler (statt der gewünschten Anzahl von verfügbaren Bytes) ausgibt, wenn der Prozess versucht zu lesen.) ZB wc >/dev/null 0>&1->wc: standard input: Bad file descriptor
Peter Cordes
1
@PeterCordes - Das Schließen des alten Deskriptors und das Umleiten des neuen müssen sich nicht gegenseitig ausschließen. exec <&- >&- <>/dev/null >&0geht ziemlich ausführlich mit stdin / out um. Es macht zshzumindest einen Unterschied, welche Art der Verkettung alle Öffnungen auf demselben Deskriptor automatisch ausführt, wenn Multios festgelegt werden.
mikeserv