Silently kill subshell?

Note:

  • wait $subshell won't work as $subshell is not a child of the process you're running wait in. Anyway, you'd not waiting for the process doing the wait so it doesn't matter much.
  • kill $subshell is going to kill the subshell but not sleep if the subshell had managed to start it by the time kill was run. You could however run sleep in the same process with exec
  • 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:

  1. the subshell
  2. sudo as a child of 1
  3. 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.