How to "correctly" start an application from a shell

I find it hard to phrase the question precisely but I will give my best. I use dwm as my default window manager and dmenu as my application launcher. I hardly use GUI applications aside from my browser. Most of my work is done directly from the command line. Furthermore, I’m a great fan of minimalism regarding operating systems, applications etc. One of the tools I never got rid of was an application launcher. Mainly because I lack a precise understanding of how application launchers work/what they do. Even extensive internet search only shows up vague explanation. What I want to do is get rid even of my application launcher because apart from actually spawning the application I have absolutely no use for it. In order to do this I would really like to know how to “correctly” start applications from the shell. Whereby the meaning of “correctly” can be approximated by “like an application launcher would do”. I do not claim that all application launchers work the same way because I do not understand them well enough.

I know about the following ways to spawn processes from the shell:

  1. exec /path/to/Program replace shell with the specified command without creating a new process
  2. sh -c /path/to/Program launch shell dependent process
  3. /path/to/Program launch shell dependent process
  4. /path/to/Program 2>&1 & launch shell independent process
  5. nohup /path/to/Program & launch shell independent process and redirect output to nohup.out

Update 1: I can illustrate what e.g. dmenu does reconstructing it from repeated calls to ps -efl under different conditions. It spawns a new shell /bin/bash and as a child of this shell the application /path/to/Program. As long as the child is around so long will the shell be around. (How it manages this is beyond me…) In contrast if you issue nohup /path/to/Program & from a shell /bin/bash then the program will become the child of this shell BUT if you exit this shell the program’s parent will be the uppermost process. So if the first process was e.g. /sbin/init verbose and it has PPID 1 then it will be the parent of the program. Here’s what I tried to explain using a graph: chromium was launched via dmenu, firefox was launched using exec firefox & exit:

systemd-+-acpid
        |-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
        |                 |                           `-chromium---5*[chromium-+-{Chrome_ChildIOT}]
        |                 |                                                    |-{Compositor}]
        |                 |                                                    |-{HTMLParserThrea}]
        |                 |                                                    |-{OptimizingCompi}]
        |                 |                                                    `-3*[{v8:SweeperThrea}]]
        |                 |-chromium
        |                 |-chromium-+-chromium
        |                 |          |-{Chrome_ChildIOT}
        |                 |          `-{Watchdog}
        |                 |-{AudioThread}
        |                 |-3*[{BrowserBlocking}]
        |                 |-{BrowserWatchdog}
        |                 |-5*[{CachePoolWorker}]
        |                 |-{Chrome_CacheThr}
        |                 |-{Chrome_DBThread}
        |                 |-{Chrome_FileThre}
        |                 |-{Chrome_FileUser}
        |                 |-{Chrome_HistoryT}
        |                 |-{Chrome_IOThread}
        |                 |-{Chrome_ProcessL}
        |                 |-{Chrome_SafeBrow}
        |                 |-{CrShutdownDetec}
        |                 |-{IndexedDB}
        |                 |-{LevelDBEnv}
        |                 |-{NSS SSL ThreadW}
        |                 |-{NetworkChangeNo}
        |                 |-2*[{Proxy resolver}]
        |                 |-{WorkerPool/1201}
        |                 |-{WorkerPool/2059}
        |                 |-{WorkerPool/2579}
        |                 |-{WorkerPool/2590}
        |                 |-{WorkerPool/2592}
        |                 |-{WorkerPool/2608}
        |                 |-{WorkerPool/2973}
        |                 |-{WorkerPool/2974}
        |                 |-{chromium}
        |                 |-{extension_crash}
        |                 |-{gpu-process_cra}
        |                 |-{handle-watcher-}
        |                 |-{inotify_reader}
        |                 |-{ppapi_crash_upl}
        |                 `-{renderer_crash_}
        |-2*[dbus-daemon]
        |-dbus-launch
        |-dhcpcd
        |-firefox-+-4*[{Analysis Helper}]
        |         |-{Cache I/O}
        |         |-{Cache2 I/O}
        |         |-{Cert Verify}
        |         |-3*[{DOM Worker}]
        |         |-{Gecko_IOThread}
        |         |-{HTML5 Parser}
        |         |-{Hang Monitor}
        |         |-{Image Scaler}
        |         |-{JS GC Helper}
        |         |-{JS Watchdog}
        |         |-{Proxy R~olution}
        |         |-{Socket Thread}
        |         |-{Timer}
        |         |-{URL Classifier}
        |         |-{gmain}
        |         |-{localStorage DB}
        |         |-{mozStorage #1}
        |         |-{mozStorage #2}
        |         |-{mozStorage #3}
        |         |-{mozStorage #4}
        |         `-{mozStorage #5}
        |-gpg-agent
        |-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
        |                               |          `-{Xorg.bin}
        |                               `-dwm-+-dwmstatus
        |                                     `-xterm---bash-+-bash
        |                                                    `-pstree
        |-systemd---(sd-pam)
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        |-wpa_actiond
        `-wpa_supplicant

Update 2: I guess the question can also be boiled down to: What should be the parent of a process? Should it e.g. be a shell or should it be the init process i.e. the process with PID 1?

Asked By: lord.garbage

||

Well, you seem to have a pretty good understanding of it. 
To clarify some of what you have,

  • sh -c /path/to/Program is fairly similar to

    $ sh
    % /path/to/Program
    % Ctrl+D                             (or you could type “exit”)
    $ 

    where you start a new shell process,
    provide the application command path to the new shell,
    and then let the new shell terminate. 
    I have shown the new shell giving a different prompt for illustration purposes;
    this probably wouldn’t happen in real life. 
    The sh -c "command" construct is mostly useful
    for doing tricky stuff, like wrapping multiple commands into a bundle,
    so they look like a single command (sort of a single-use unnamed script),
    or building complicated commands, possibly from shell variables. 
    You would hardly ever use it just for running a single program with simple argument(s).

  • 2>&1 means redirect the standard error to the standard output.
    This doesn’t really have much to do with &;
    rather, you use it when a command sends error messages to the screen
    even if you say command > file
    and you want to capture the error messages in the file.
  • Redirecting output to nohup.out is a trivial side-effect of nohup
    The primary purpose of nohup command &
    is to run command asynchronously
    (commonly known as “in the background”,
    or as a “shell independent process”, to use your words)
    and configure it so it has a better chance of being able to continue to run
    if you terminate the shell (e.g., logout) while the command is still running.

bash(1) and the Bash Reference Manual
are good sources of information.

There are a few ways way to execute a program and detach it from a terminal. One is to run it in the background of a subshell, like this (replace firefox with your favorite program):

(firefox &)

Another is to disown the process:

firefox & disown firefox

If you are curious about how app launchers work, dmenu provides 1 binary and 2 shell scripts: dmenu, dmenu_path and dmenu_run, respectively.

dmenu_run pipes the output of dmenu_path into dmenu, which in turn pipes into whatever your $SHELL variable is set to. If it is empty, it will use /bin/sh.

#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

dmenu_path is a bit more complex, but in short it provides a list of binaries in your $PATH environment variable, and uses a cache if possible.

#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
        cache=$cachedir/dmenu_run
else
        cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
IFS=:
if stest -dqr -n "$cache" $PATH; then
        stest -flx $PATH | sort -u | tee "$cache"
else
        cat "$cache"
fi

It isn’t necessary to have programs running in shells. Another way to write dmenu_run, without piping into a shell would be:

#!/bin/sh
$(dmenu_path | dmenu "$@") &
Answered By: gnucchi

I like G-Man’s answer a lot. But I’m responding because I think you are confusing concerns. As Wayne points out, the best answer is “whatever gets the results you want”.

In Unix process management, every process has a parent. The one exception to this is the init process which is started by the OS at boot. It is normal behavior for a parent process to take all of it’s child processes with it when it dies. This is done by sending the SIGHUP signal to all child processes; the default handling of SIGHUP terminates the process.

The shell spawning of user processes is no different than if you coded the fork(2)/exec(3) calls in the language of your choice. The shell is your parent, and if the shell terminates (e.g., you log off), then the child process(es) it spawns goes with it. The nuances you describe are just ways of modifying that behavior.

exec /path/to/program is like calling exec(3). Yes, it will replace your shell with program, keeping whatever parent launched the shell.

sh -c /path/to/program kind of pointlessly creates a child shell process that will create a child process of program. It is only valuable if /path/to/program is actually a sequence of script instructions, and not an executable file. (sh /path/to/script.sh can be used to run a shell script that lacks execute permissions in an inferior shell)

/path/to/program creates a “foreground” process, meaning that the shell waits for the process to complete before taking any other action. In system call context, it is like fork(2)/exec(3)/waitpid(2). Note that the child inherits stdin/stdout/stderr from the parent.

/path/to/program & (ignoring redirection) creates a “background process”. The process is still a child of the shell, but the parent is not waiting for it to terminate.

nohup /path/to/program invokes nohup(1) to prevent the sending of SIGHUP to program if the controlling terminal is closed. Whether it is in the foreground or background is a choice (although most commonly the process is backgrounded). Note that nohup.out is only the output if you don’t otherwise redirect stdout.

When you put a process in the background, if the parent process dies, one of two things happens. If the parent is a controlling terminal, then SIGHUP will be sent to the children. If it is not, then the process may be “orphaned”, and is inherited by the init process.

When you redirect input/output/error, you are simply connecting the file descriptors that every process has to different files than the ones it inherits from its parent. None of this affects process ownership or tree depth (but it always makes sense to redirect all 3 away from a terminal for background processes).

With all that said, I don’t think you should be concerned with process creation by the shell or sub-shells or sub-processes, unless there is a specific problem you are addressing related to process management.

Answered By: jwm
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.