Search code examples
bashloggingiofile-descriptortee

Duplicate IO with file descriptors


I would like to route a file descriptor to multiple places at the same time. For instance I would like every command in my script to print stdout to /dev/ps/9 and ./myscript.stdout at the same time.

I'm looking to achieve similar results as piping every command in a script (or a section of a script) into tee, perhaps with file descriptors. I also want the ability to restore default output behavior later in the script.

This code doesn't work, but it's an attempt at expressing my intent. To restore stdout as FD 1 later, I copy it into FD 4.

exec 3>(tee /dev/ps/9 ./myscript.stdout)
exec 4>&1
exec 1>&3

Restore normal output behavior, deleting FDs 3 and 4.

exec 1>&4
exec 4>&-
exec 3>&-

Solution

  • I would like every command in my script to print stdout to /dev/ps/9 and ./myscript.stdout at the same time.

    exec 1> >(tee ./myscript.stdout >/dev/ps/9)
    

    The above combines redirection and process substitution. With redirection alone, one can send stdout to a file. For example:

    exec 1> filename
    

    However, with bash, filenames can often be replaced with commands. This is called process substitution and it looks like >(some command) or <(some command) depending on whether one wants to write-to or read-from the process. In our case, we want to write to a tee command. Thus:

    exec 1> >(some command)
    

    Or, more specifically:

    exec 1> >(tee ./myscript.stdout >/dev/ps/9)
    

    Note that we have to maintain the space between the redirect (1>) and the process substitution (>(tee ./myscript.stdout >/dev/ps/9). Without the space, it would look like we were trying to append to a file whose name starts with a parens and this would generate a bash error.

    For more information on this see the sections entitled "REDIRECTION" and "Process Substitution" in man bash.