Bash Tip: Closing File Descriptors

I recently found that you can close bash file descriptors fairly easily, it goes like this:

    exec 0>&- # close stdin
    exec 1>&- # close stdout
    exec 2>&- # close stderr

Which makes it easy to daemonize things using only bash (lets face it there are times when you JUST don’t need anything more than a simple bash script, you just need it backgrounded/daemonized). Take this example of a daemon that copies any new files created in a directory to another place on the filesystem

#!/bin/bash

##
## Shell Daemon For: Backup /root/
## (poorly coded, quick and dirty, example)
##

PIDFILE="/var/run/rootmirror.pid"
LOGFILE="/log/log/rootmirror-%Y-%m-%d.log"
NOHUP="/usr/bin/nohup"
CRONOLOG="/usr/bin/cronolog"
case $1 in
start)
    exec 0>&- # close stdin
    exec 1>&- # close stdout
    exec 2>&- # close stderr
    $NOHUP $0 run | $CRONOLOG $LOGFILE >> /dev/null &
    ;;
stop)
    /bin/kill $(cat $PIDFILE)
    ;;
run)
    pgrep -f "$0 $1" > $PIDFILE
    while [ true ]; do
        event=$(inotifywait -q -e close_write --format "%f" /root/)
        ( cp -v "/root/$event" "/var/lib/rootmirror/$event" )&
    done
    ;;
*)
    echo "$0 [ start | stop ]"
    exit 0
    ;;
esac

One especially nice detail here is that this wont hang while exiting your SSH session after you start it up (a big pet peeve of mine).

8 thoughts on “Bash Tip: Closing File Descriptors

  1. Yeah, this is broken:

    exec 3>&- # close stdin

    exec 2>&- # close stdout

    exec 1>&- # close stderr

    It should be

    exec 0>&- # close stdin

    exec 1>&- # close stdout

    exec 2>&- # close stderr

  2. foo says:

    why not just start up screen and while true;do sh myscript.sh; done ?

    then attach to the screen session if you ever want to close it. Then you don't ever have to worry about the PID getting recycled, and your script accidentally closing the wrong process!

    • @foo Granted. But sometimes screen is not necessary or wanted. checking the PIDfile contents versus the running process list is, naturally, a good idea. But the post was about closing the file descriptors, not writing idfiles or killing processes, so the example is just that: an example.

  3. Dennis Birkholz says:

    You posted this quite some time ago but i have a little improvment for you: use $$ in bashto get the current process id, not pgrep. Otherwise, interesting example.

    Greets,
    Dennis

  4. Christopher M says:

    And one more comment on an old thread… Bash has a wonderful built-in called "disown" that fully disconnects from a process. You can do do something like this:

    program &
    disown %1

    This completely disconnects it from all open channels and makes it very easy to launch and forget.

Leave a Reply to ndronenCancel reply