Search code examples
bashfile-descriptor

How can I redefine stdout/stderr in bash?


Is it possible to redefine file descriptors (e.g. stderr) in bash?

I would like to send all output to a file by default while still being able to use the original stderr and stdout.

#!/bin/bash
echo "Error: foo bar" 1>2
REAL_STDERR=2
REAL_STDOUT=1
2=open("/tmp/stderr.log")
1=open("/tmp/stdout.log")
echo "This goes to stdout.log"
if ! curl doesntexist.yet; then
    echo "Error: Unable to reach host. See stderr.log for details" 1>REAL_STDERR
fi

Solution

  • The exec builtin does this when not given a name of a command to run.

    exec 3>&1 4>&2 2>/tmp/stderr.log >/tmp/stdout.log
    
    echo "This goes to stdout.log"
    echo "This goes to stderr.log" >&2
    
    echo "This goes directly to real stderr" >&4
    

    Note that redirections are processed in the order they're given on the command line left-to-right. Thus, &1 and &2 are interpreted as-modified by any previous redirections on the same command.

    See the relevant POSIX spec.


    If you want to use variable names for your file descriptors (with the numbers automatically allocated), you'll need bash 4.1 or newer. There, you can do:

    exec {real_stderr}>&2 {real_stdout}>&1 >stdout.log 2>stderr.log
    echo "This goes stdout.log"
    echo "This goes to stderr.log" >&2
    echo "This goes to real stderr" >&$real_stderr