Skip to content

Instantly share code, notes, and snippets.

@dualbus
Last active November 26, 2020 17:48
Show Gist options
  • Save dualbus/9275406 to your computer and use it in GitHub Desktop.
Save dualbus/9275406 to your computer and use it in GitHub Desktop.
How to detach processes from bash properly.
Relevant section from ''man bash'', SIGNALS:
> The shell exits by default upon receipt of a SIGHUP. Before exiting, an interactive shell resends the SIGHUP to all jobs,
> running or stopped. Stopped jobs are sent SIGCONT to ensure that they receive the SIGHUP. To prevent the shell from send‐
> ing the signal to a particular job, it should be removed from the jobs table with the disown builtin (see SHELL BUILTIN
> COMMANDS below) or marked to not receive SIGHUP using disown -h.
>
> If the huponexit shell option has been set with shopt, bash sends a SIGHUP to all jobs when an interactive login shell
> exits.
According to this, the proper way to detach a process from bash is the following:
$ command >/dev/null 2>&1 <&1 & disown
The redirections are needed. If you don't redirect to /dev/null, the
process will inherit the shell's descriptors, which will point to the
terminal. When this terminal closes, the descriptors will point to a
closed file, which will make the process crash if it tries to write
to them.
The disown is needed, because under some conditions the shell will
send a SIGHUP to all the jobs in the job table, which will kill the
"detached" process. And instead of ignoring SIGHUP in the detached
process, let's not send the signal to it in the first place.
+ bash=/bin/bash
+ for flags in -c -ic -imc
+ /bin/bash -c '(echo :) & wait'
/bin/bash: line 0: echo: write error: Bad file descriptor
+ /bin/bash -c '(echo :) >/dev/null 2>&1 </dev/null & wait'
+ /bin/bash -c '(echo :) & disown; wait'
/bin/bash: line 0: echo: write error: Bad file descriptor
+ /bin/bash -c '(echo :) >/dev/null 2>&1 </dev/null & disown; wait'
+ SECONDS=0
+ /bin/bash -c '{ sleep 5; echo a; } & disown; wait'
+ echo a0
a0
+ SECONDS=0
+ /bin/bash -c '{ sleep 5; echo b; } & wait'
a
b
+ echo b5
b5
+ p=20825
+ exec /bin/bash -c '{ trap "echo a" HUP; sleep 5; } & wait'
+ sleep 3
+ kill -HUP 20825
script: line 8: 20825 Hangup ( p=$BASHPID; { sleep 3; kill -HUP "$p"; } & exec "$bash" "$flags" '{ trap "echo a" HUP; sleep 5; } & wait' )
+ p=20847
+ exec /bin/bash -c '{ trap "echo a" HUP; sleep 5; } & disown; wait'
+ for flags in -c -ic -imc
+ sleep 3
+ /bin/bash -ic '(echo :) & wait'
[1] 20854
/bin/bash: line 0: echo: write error: Bad file descriptor
[1]+ Exit 1 ( echo : )
+ /bin/bash -ic '(echo :) >/dev/null 2>&1 </dev/null & wait'
[1] 20856
[1]+ Done ( echo : ) > /dev/null 2>&1 < /dev/null
+ /bin/bash -ic '(echo :) & disown; wait'
[1] 20858
/bin/bash: line 0: echo: write error: Bad file descriptor
+ /bin/bash -ic '(echo :) >/dev/null 2>&1 </dev/null & disown; wait'
[1] 20860
+ SECONDS=0
+ /bin/bash -ic '{ sleep 5; echo a; } & disown; wait'
[1] 20863
+ echo a0
a0
+ SECONDS=0
+ /bin/bash -ic '{ sleep 5; echo b; } & wait'
[1] 20867
+ kill -HUP 20847
script: line 19: kill: (20847) - No such process
a
b
[1]+ Done { sleep 5; echo b; }
+ echo b5
b5
+ p=20896
+ exec /bin/bash -ic '{ trap "echo a" HUP; sleep 5; } & wait'
+ sleep 3
[1] 20899
+ kill -HUP 20896
Hangup
a
+ p=20919
+ exec /bin/bash -ic '{ trap "echo a" HUP; sleep 5; } & disown; wait'
+ sleep 3
[1] 20922
+ for flags in -c -ic -imc
+ /bin/bash -imc '(echo :) & wait'
[1] 20925
/bin/bash: line 0: echo: write error: Bad file descriptor
[1]+ Exit 1 ( echo : )
+ /bin/bash -imc '(echo :) >/dev/null 2>&1 </dev/null & wait'
[1] 20927
[1]+ Done ( echo : ) > /dev/null 2>&1 < /dev/null
+ /bin/bash -imc '(echo :) & disown; wait'
[1] 20929
/bin/bash: line 0: echo: write error: Bad file descriptor
+ /bin/bash -imc '(echo :) >/dev/null 2>&1 </dev/null & disown; wait'
[1] 20931
+ SECONDS=0
+ /bin/bash -imc '{ sleep 5; echo a; } & disown; wait'
[1] 20934
+ echo a0
a0
+ SECONDS=0
+ /bin/bash -imc '{ sleep 5; echo b; } & wait'
[1] 20938
+ kill -HUP 20919
script: line 19: kill: (20919) - No such process
a
b
[1]+ Done { sleep 5; echo b; }
+ echo b5
b5
+ p=20970
+ exec /bin/bash -imc '{ trap "echo a" HUP; sleep 5; } & wait'
+ sleep 3
[1] 20973
+ kill -HUP 20970
script: line 8: 20970 Hangup ( p=$BASHPID; { sleep 3; kill -HUP "$p"; } & exec "$bash" "$flags" '{ trap "echo a" HUP; sleep 5; } & wait' )
Hangup
a
+ p=20993
+ exec /bin/bash -imc '{ trap "echo a" HUP; sleep 5; } & disown; wait'
+ sleep 3
[1] 20996
+ kill -HUP 20993
script: line 19: kill: (20993) - No such process
#!/bin/bash
set -x
bash=/bin/bash
# Do not run with -mc, or it'll bash you!
for flags in -c -ic -imc; do
("$bash" "$flags" '(echo :) & wait' >&-)
("$bash" "$flags" '(echo :) >/dev/null 2>&1 </dev/null & wait' >&-)
("$bash" "$flags" '(echo :) & disown; wait' >&-)
("$bash" "$flags" '(echo :) >/dev/null 2>&1 </dev/null & disown; wait' >&-)
(SECONDS=0; "$bash" "$flags" '{ sleep 5; echo a; } & disown; wait'; echo "a$SECONDS")
(SECONDS=0; "$bash" "$flags" '{ sleep 5; echo b; } & wait'; echo "b$SECONDS")
(p=$BASHPID; { sleep 3; kill -HUP "$p"; } & exec "$bash" "$flags" '{ trap "echo a" HUP; sleep 5; } & wait')
(p=$BASHPID; { sleep 3; kill -HUP "$p"; } & exec "$bash" "$flags" '{ trap "echo a" HUP; sleep 5; } & disown; wait')
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment