Monday, March 14, 2011

Background processes and file descriptors in shell scripts

Lately I stumbled upon an issue in a shell script which left me puzzling for a while.

Reduced to a simple example it goes like this:
envision you have two files and whereas is supposed to call in backticks:
x=``      # run and collect its output    
echo x=$x
(sleep 60)&        # start a background process
echo pid=$!        # report the pid of the background process
exit 0

The expected output of was x=pid=12345 kind of immediately after running it.

The unexpected but experienced behaviour was that was waiting until the background process had finished. This was defying the purpose of the script since in the original scenario should have managed (like sending signals) the background process after doing some work in between.

Some experimenting with variations of the scripts and some reading finally revealed the clue to the issue.
  • Background (better: forked) processes inherit the file descriptors of their parent process
    i.e. the 'sleep' background process has the same open fds as
  • Running a command in backticks means to collect its stdout until its stdout is closed
    i.e. waits until the stdout of is closed for good.
  • Since the 'sleep' background process writes to the same stdout as the fd is kept open even after has finished.
    It does not matter if 'sleep' is actually writing anything or not, the point is that if it would write something it would write to the inherited open stdout.
    ('sleep' is just an example. In the real world it would very likely be another script with some complex tasks to fulfil).
  • The solution is to close stdout of the background process
    (exec >&-; sleep 60)&  # start a background process but close stdout first
    echo pid=$!            # report the pid of the background process
    exit 0

Some hints to explain the situation is the process table showing that has a defunct sub process (the former and the 'sleep' process is a child of init (pid 1). Also a slightly different sub process (echo sub; sleep 60)& leads to x=pid=12345 sub thus showing that gathered the output of plus the output of the the sub process.

I wonder how many people are paying attention to this, it is an issue which can be easily overlooked. In essence background processes in scripts like are daemons since gives up control of the sub process by simple exiting at some point. So who controls the sub processes, in particular where should they write their output to? Rereading the essentials of a daemon process helps and I will definitly pay more attention to this in the future.

An experiment for the curious:
what happens if stdout was redirected to a file and multiple sub processes were started, each writing to stdout aka. the file? Would everything be written to the file? In which order?
exec 1>/tmp/out
(for i in 1 2 3 4 5; do echo aaaaaaaa; sleep 1 ; done)&
(for i in 1 2 3 4 5; do echo bbbbbbbb; sleep 1 ; done)&
echo DONE


  1. Interesting issue! Thanks for your experiments and explanation :)

  2. Came here from , helped a lot, thanks!