'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 1:[1]

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.

Solution 2:[2]

#!/bin/bash

random=$$ # generate a random seed number to name the log files with
out=out.$random
err=err.$random
dev=`echo $(who -m) | cut -d' ' -f2` # for finding the right pseudo-terminal
: >$out # create the log files or empty their contents
: >$err # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

exec 1> >(tee ./$out >/dev/$dev) # I don't know how this works but it does
exec 2> >(tee ./$err >/dev/$dev) # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
echo # writing directly to the pts in /dev doesn't look right until sending a blank line

##########################################

echo 'hello'

for i in `seq 0 1 10`; do
    echo $i
done

bad_command

THANKS @John1024

Here's a script for anybody else wishing to test this out.

Could somebody please explain in detail the exec lines to me.

For instance, why is there a blank space after the arrow in:

exec 1> 

?

Solution 3:[3]

#!/bin/bash
logfile=$$.log
exec > $logfile 2>&1 | tee
echo "test"

The $$ does a random seed number which is optional.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 PhysicalChemist