Silently kill subshell?
Note:
wait $subshell
won't work as$subshell
is not a child of the process you're runningwait
in. Anyway, you'd not waiting for the process doing thewait
so it doesn't matter much.kill $subshell
is going to kill the subshell but notsleep
if the subshell had managed to start it by the timekill
was run. You could however runsleep
in the same process withexec
- you can use SIGPIPE instead of SIGTERM to avoid the message
- leaving a variable unquoted in list contexts has a very special meaning in
bash
.
So having said all that, you can do:
(
subshell=$BASHPID
kill -s PIPE "$subshell" &
sleep 600
)
echo subshell done
(replace sleep 60
with exec sleep 60
if you want the kill
to kill sleep
and not just the subshell, which in this case might not even have time to run sleep
by the time you kill it).
In any case, I'm not sure what you want to achieve with that.
sleep 600 &
would be a more reliable way to start sleep
in background if that's what you wanted to do (or (sleep 600 &)
if you wanted to hide that sleep
process from the main shell)
Now with your actual
sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf
command, note that sudo
does spawn a child process to run the command (if only because it may need to log its status or perform some PAM session tasks afterwards). stdbuf
will however execute wpa_supplicant
in the same process, so in the end you'll have three processes (in addition to the rest of the script) in wpa_supplicant
's ancestry:
- the subshell
- sudo as a child of 1
- wpa_supplicant (which was earlier running stdbuf) as a child of 2
If you kill 1, that doesn't automatically kills 2. If you kill 2 however, unless it's with a signal like SIGKILL that can't be intercepted, that will kill 3 as sudo
happens to forward the signals it receives to the command it runs.
In any case, that's not the subshell you'd want to kill here, it's 3 or at least 2.
Now, if it's running as root
and the rest of the script is not, you won't be able to kill it so easily.
You'd need the kill
to be done as root
, so you'd need:
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"
}'
That way, wpa_supplicant
will be running in the same $BASHPID
process as the subshell as we're making of that with exec
.
We get the pid through the pipe and run kill
as root.
Note that if you're ready to wait a little longer,
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"
Would have wpa_supplicant
killed automatically with a SIGPIPE (by the system, so no permission issue) the next time it writes something to that pipe after grep
is gone.
Some shell implementations would not wait for sudo
after grep
has returned (leaving it running in background until it gets SIGPIPEd), and with bash
, you can also do that using the grep ... <(sudo ...)
syntax, where bash
doesn't wait for sudo
either after grep
has returned.
More at Grep slow to exit after finding match?
Subshell refers to a shell command that is a child of some shell, such as child of the bash -i
interactive login shell that offers you a $
prompt. You don't have to run your command in a subshell - you could choose to run it as an independent process. It sounds like this may be appropriate because you don't want its stdout / stderr messing up the appearance of your progress bar, and because you don't want the parent shell to report on or even notice the death of its child.
There are standard tools for accomplishing that, such as daemonize and nohup. (See also man pages.) You may be best off with nohup. Here is an example of using it to run a trivial program, which does not create nohup.out:
$ nohup true 2>&1 > /dev/null &
Have your program, or a wrapper script for your program, record its PID in /tmp/my.pid -- bash makes that available as the $$
variable. Then the monitoring process with the progress bar can
$ kill `cat /tmp/my.pid`
when it no longer needs that program to do any more processing. Alternatively, you might prefer to give your program name to killall
.