Subshell lautlos beenden?

Subshell lautlos beenden?

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 doneFolgendes 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 $subshellfunktioniert nicht, da $subshelles sich nicht um ein untergeordnetes Element des Prozesses handelt, waitden Sie ausführen. Da Sie sowieso nicht auf den Prozess warten, der waitdies ausführt, spielt es keine große Rolle.
  • kill $subshellwird die Subshell beenden, aber nicht sleep, wenn die Subshell es zum Zeitpunkt killder Ausführung geschafft hatte, sie zu starten. Sie könnten jedoch sleepim 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 60durch exec sleep 60, wenn Sie möchten, dass die Shell killbeendet wird sleepund nicht nur die Subshell, die in diesem Fall möglicherweise nicht einmal Zeit hat, ausgeführt zu werden, sleepwenn Sie sie beenden).

Ich bin mir jedenfalls nicht sicher, was Sie damit erreichen wollen.

sleep 600 &

wäre eine zuverlässigere Möglichkeit, sleepim 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 sudobeim 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). stdbufwird jedoch im selben Prozess ausgeführt wpa_supplicant, sodass Sie am Ende drei Prozesse (zusätzlich zum Rest des Skripts) in wpa_supplicantder Abstammungslinie von haben:

  1. die Unterschale
  2. sudo als Kind von 1
  3. 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 sudodie 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, rootder Rest des Skripts jedoch nicht, können Sie es nicht so einfach beenden.

Das müsste killwie 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_supplicantwird es im selben $BASHPIDProzess wie die Subshell ausgeführt, die wir damit erstellen exec.

Wir holen die PID über die Pipe und führen sie killals 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_supplicantautomatisch mit einem SIGPIPE beendet (durch das System, also kein Berechtigungsproblem)nächstes Males schreibt etwas in diese Pipe, nachdem grepes weg ist.

Einige Shell-Implementierungen würden nicht auf die Rückgabe sudonach grepder Rückkehr warten (und es im Hintergrund laufen lassen, bis es SIGPIPEd wird). Mit bashkönnen Sie dies auch mit der grep ... <(sudo ...)Syntax tun, wobei auch bashnicht auf die Rückgabe sudonach grepder 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 -iinteraktiven 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 TerminatedNachricht abgefangen.

Beachten Sie, wenn Sie „wait to capture output to file“ ändern, wait $! 2>wait_outputdann sehen Sie

 ./foo.sh: line 5:  1939 Terminated              ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )

zeigt an, dass Terminatedes 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 TerminatedNachrichten 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. waitDies geschieht durch die Umleitung der Befehlsausgabe.

verwandte Informationen