Ich möchte so etwas umsetzenFragen und Antwortenaber für eine Unterschale. Hier ist ein Minimalbeispiel für das, was ich versuche:
(subshell=$BASHPID
(kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)
echo subshell done
Wie kann ich es so einrichten, dass nur subshell done
Folgendes zurückgegeben wird:
./test.sh: line 4: 5439 Terminated ( subshell=$BASHPID; ( kill $subshell && wait $subshell 2> /dev/null ) & sleep 600 )
subshell done
Bearbeiten: Möglicherweise irre ich mich hinsichtlich der Terminologie, mit Subshell meine ich den Prozess innerhalb der ersten Klammern.
Aktualisieren:
Ich möchte den Ausschnitt aus dem eigentlichen Programm zum Kontext posten, oben ist eine Vereinfachung:
# If subshell below if killed or returns error connected variable won't be set
(if [ -n "$2" ];then
# code to setup wpa configurations here
# If wifi key is wrong kill subshell
subshell=$BASHPID
(sudo stdbuf -o0 wpa_supplicant -Dwext -i$wifi -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 \
| grep -m 1 "pre-shared key may be incorrect" \
&& kill -s PIPE "$subshell") &
# More code which does the setup necessary for wifi
) && connected=true
# later json will be returned based on if connected is set
Antwort1
Notiz:
wait $subshell
funktioniert nicht, da$subshell
es sich nicht um ein untergeordnetes Element des Prozesses handelt,wait
den Sie ausführen. Da Sie sowieso nicht auf den Prozess warten, derwait
dies ausführt, spielt es keine große Rolle.kill $subshell
wird die Subshell beenden, aber nichtsleep
, wenn die Subshell es zum Zeitpunktkill
der Ausführung geschafft hatte, sie zu starten. Sie könnten jedochsleep
im selben Prozess mitexec
- Sie können SIGPIPE anstelle von SIGTERM verwenden, um die Meldung zu vermeiden
- Das Lassen einer Variable ohne Anführungszeichen in Listenkontexten hat in eine ganz besondere Bedeutung
bash
.
Nachdem das alles gesagt ist, können Sie Folgendes tun:
(
subshell=$BASHPID
kill -s PIPE "$subshell" &
sleep 600
)
echo subshell done
(Ersetzen Sie es sleep 60
durch exec sleep 60
, wenn Sie möchten, dass die Shell kill
beendet wird sleep
und nicht nur die Subshell, die in diesem Fall möglicherweise nicht einmal Zeit hat, ausgeführt zu werden, sleep
wenn Sie sie beenden).
Ich bin mir jedenfalls nicht sicher, was Sie damit erreichen wollen.
sleep 600 &
wäre eine zuverlässigere Möglichkeit, sleep
im Hintergrund zu starten, wenn Sie das tun möchten (oder wenn Sie diesen Prozess vor der Haupt-Shell (sleep 600 &)
verbergen möchten )sleep
Jetzt mit Ihrem tatsächlichen
sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf
Beachten Sie, dass sudo
beim Ausführen des Befehls ein untergeordneter Prozess erstellt wird (schon allein, weil dieser möglicherweise seinen Status protokollieren oder anschließend einige PAM-Sitzungsaufgaben ausführen muss). stdbuf
wird jedoch im selben Prozess ausgeführt wpa_supplicant
, sodass Sie am Ende drei Prozesse (zusätzlich zum Rest des Skripts) in wpa_supplicant
der Abstammungslinie von haben:
- die Unterschale
- sudo als Kind von 1
- wpa_supplicant (auf dem vorher stdbuf ausgeführt wurde) als Kind von 2
Wenn Sie 1 beenden, wird nicht automatisch auch 2 beendet. Wenn Sie jedoch 2 beenden, wird 3 beendet, es sei denn, dies geschieht mit einem Signal wie SIGKILL, das nicht abgefangen werden kann, da es sudo
die empfangenen Signale an den von ihm ausgeführten Befehl weiterleitet.
Das ist auf jeden Fall nicht die Unterschale, die Sie hier zerstören möchten, sondern 3 oder zumindest 2.
Wenn es nun so ausgeführt wird, root
der Rest des Skripts jedoch nicht, können Sie es nicht so einfach beenden.
Das müsste kill
wie folgt durchgeführt werden root
, Sie benötigen also:
sudo WIFI="$wifi" bash -c '
(echo "$BASHPID" &&
exec stdbuf -o0 wpa_supplicant -Dwext -i"$WIFI" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1
) | {
read pid &&
grep -m1 "pre-shared key may be incorrect" &&
kill -s PIPE "$pid"
}'
Auf diese Weise wpa_supplicant
wird es im selben $BASHPID
Prozess wie die Subshell ausgeführt, die wir damit erstellen exec
.
Wir holen die PID über die Pipe und führen sie kill
als Root aus.
Wenn Sie bereit sind, noch etwas zu warten,
sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 |
grep -m1 "pre-shared key may be incorrect"
Hätte wpa_supplicant
automatisch mit einem SIGPIPE beendet (durch das System, also kein Berechtigungsproblem)nächstes Males schreibt etwas in diese Pipe, nachdem grep
es weg ist.
Einige Shell-Implementierungen würden nicht auf die Rückgabe sudo
nach grep
der Rückkehr warten (und es im Hintergrund laufen lassen, bis es SIGPIPEd wird). Mit bash
können Sie dies auch mit der grep ... <(sudo ...)
Syntax tun, wobei auch bash
nicht auf die Rückgabe sudo
nach grep
der Rückkehr gewartet wird.
Mehr beiWird Grep nach dem Finden einer Übereinstimmung langsam beendet?
Antwort2
Subshell bezieht sich auf einen Shell-Befehl, der ein Kind einer Shell ist, wie z. B. ein Kind der bash -i
interaktiven Login-Shell, die Ihnen eine Eingabeaufforderung bietet $
. Sie müssen nichthabenum Ihren Befehl in einer Subshell auszuführen - Sie könnten ihn als unabhängigen Prozess ausführen. Das klingt, als wäre das angemessen, weil Sie nicht möchten, dass sein stdout/stderr das Erscheinungsbild Ihres Fortschrittsbalkens durcheinander bringt, und weil Sie nicht möchten, dass die übergeordnete Shell den Tod ihres untergeordneten Prozesses meldet oder gar bemerkt.
Dafür gibt es Standardwerkzeuge wiedämonisierenund nohup. (Siehe auchMann Seiten.) Am besten sind Sie mitnein. Hier ist ein Beispiel für die Verwendung zum Ausführen eines trivialen Programms, dasnichtnohup.out erstellen:
$ nohup true 2>&1 > /dev/null &
Lassen Sie Ihr Programm oder ein Wrapper-Skript für Ihr Programm dessen PID in /tmp/my.pid aufzeichnen – Bash macht dies als $$
Variable verfügbar. Dann kann der Überwachungsprozess mit dem Fortschrittsbalken
$ kill `cat /tmp/my.pid`
wenn das Programm keine weiteren Verarbeitungen mehr benötigt. Alternativ können Sie Ihren Programmnamen auch an . vergeben killall
.
Antwort3
Vielleicht suchen Sie danach
#!/bin/bash
(subshell=$BASHPID
(kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) &
wait $! 2>/dev/null
echo subshell done
hier wird die Subshell in den Hintergrund gestellt, dann wartet die übergeordnete Shell, die Warteausgabe wird jedoch an /dev/null gesendet. Dadurch wird die Terminated
Nachricht abgefangen.
Beachten Sie, wenn Sie „wait to capture output to file“ ändern, wait $! 2>wait_output
dann sehen Sie
./foo.sh: line 5: 1939 Terminated ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )
zeigt an, dass Terminated
es von der übergeordneten Shell stammt.
Eine kleine Überprüfung, ob es funktioniert, wenn vor dem Kill eine gewisse Aktivität stattfindet, ist
#!/bin/bash
(subshell=$BASHPID
(sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output
echo subshell done
Dieses Beispiel hält eine Sekunde an, bevor es gedruckt wird subshell done
. Dieses Beispiel zeigt auch, wie man Hintergrund und Warten in derselben Zeile ausführen kann, z. B. & wait 2>wait_output
. Ich bin nicht sicher, ob das gegenüber dem Beispiel mit Vor-/Nachteilen hat wait $!
.
Der entscheidende Punkt hierbei ist, dass die Terminated
Nachrichten von der übergeordneten Shell-Jobsteuerung auf oberster Ebene stammen. Diese sorgt dafür, dass die Subshell beendet wird und generiert die Nachricht. Dort möchten Sie also die Ausgabe abfangen. wait
Dies geschieht durch die Umleitung der Befehlsausgabe.