On writing a Linux shell script to safely detach programs from a terminal

bash, scripting, shell

Solution

Upon closer investigation, these previously unnoticed facts were revealed:

Both scripts 3 and 5 (the `setsid` variant only) will satisfy all the conditions if a `/bin/true` is appended to the script.

These scripts, as modified in fact 1, will work as well if `/bin/true` is replaced with `for i in {0..9999}; do :; done`.

Therefore we can conclude that:

(From fact 1)

Multiple levels of detaching (as in script 5) is unnecessary, and the key is to use the right utility (`setsid`).

(From fact 2)

A suitable delay before bash exit is necessary for the success of the script. (Calling external program `/bin/true` consumes some time, just like the pure-bash time consumer `for i in {0..9999}; do :; done`.)

I have not looked at the source code, but I guess a possible explanation is that bash may exit before `setsid` finishes configuring the execution environment of the program to run, if an appropriate delay is not applied.

And finally, an optimal solution should be

#!/bin/bash
setsid "$@" >& /dev/null &
sleep 0.01

EDIT 1:

The necessity of a delay has been explained here. Many thanks to @wilx!

EDIT 2:

(Thanks to @MateiDavid) we seem to have forgotten to redirect the standard input, and a better way would be:

#!/bin/bash
setsid "$@" >& /dev/null < /dev/null &

Problem

I'm trying to write a Linux shell script (preferably bash), supposedly named `detach.sh`, to safely detach programs from a terminal, such that: Invocation: `./detach.sh prog [arg1 arg2 ...]`. Is `exec`-able, eg. by running this in your shell: ``` exec ./detach.sh prog [arg1 arg2 ...] ``` With proper quoting (mainly handling of arguments containing whitespaces). Discards the outputs (since they are unneeded). Does not use `screen`, `tmux`, etc. (same reason with 4, plus no need for an extra babysitting process). Uses (reasonably) portable commands and programs, and no things like `start-stop-daemon` which is quite distro-specific. I have thought of several ways (shebang lines `#!/bin/bash` neglected for the sake of briefness): `nohup`: ``` nohup "$@" >& /dev/null & ``` `disown`: ``` "$@" >& /dev/null & disown ``` `setsid`: ``` setsid "$@" >& /dev/null & ``` Using a subshell: ``` ("$@" >& /dev/null &) ``` `nohup`/`setsid` combined with subshell: ``` # Or alternatively: # (nohup "$@" >& /dev/null &) (setsid "$@" >& /dev/null &) ``` When using `gedit` as the test program (substituting the `"$@"` part), condition 1 can be satisfied with all the above methods, but condition 2 can be satisfied with none. However, if an arbitrary program (but not a shell builtin) is appended to script 5, all the conditions seem to be satisfied (at least for me in the `gedit` case). For example: ``` (setsid "$@" >& /dev/null &) # Not just `true' because it is also a shell builtin. /bin/true ``` Anyone with an idea about an explanation of the above phenomenons and how to correctly implement the requirements? EDIT: With condition 2, I mean the program should be detached from the terminal but runs as usual otherwise. For example, with the `gedit` case, the condition fails if `gedit` just exits immediately right after the process of the script has ended.

Original source