Home > Software design >  Create file descriptor which outputs to multiple targets
Create file descriptor which outputs to multiple targets

Time:05-16

I'm writing a script, and the majority of its output is best sent to the log file, but I would like to 'opt in' certain commands to console output, e.g. explicit script status/feedback messages.

I have created three named file descriptors: one for the terminal, one for the log, and one for both the terminal and log. I have tried to use tee to send input to $both and have it reach both $term and $log, but this seems to have the effect of reordering the output. Is there a way to achieve the above but preserve the output order?

# Create FD 'term' with target == stdout's current target
exec {term}>&1

# Create FD 'log' with target == ./my-log.txt
exec {log}>"$log_path"

# Attempt to use tee to split output off into $log FD and send stdout to $term FD
exec {both}> >(tee -a "/dev/fd/$log" 1>&$term)

# Set stdout and stderr to route to $log FD
exec 1>&$log 2>&$log

            echo 1 - example output of command
1>&$both    echo 2 - script feedback/status
            echo 3 - example output of command
1>&$both    echo 4 - script feedback/status
            echo 5 - example output of command

# stdout:
#   2 - script feedback/status
#   4 - script feedback/status

# $log_path:
#   1 - example output of command
#   3 - example output of command
#   5 - example output of command
#   2 - script feedback/status
#   4 - script feedback/status

Thanks

CodePudding user response:

The problem is that when you send output to both, bash doesn't wait until tee actually writes it out before it moves on to the next line, so you end up with a race condition of bash and tee making unsynchronized writes to log. Unless you have a good reason to need a single FD, the simplest and best solution is probably to just do this instead:

exec {term}>&1 >"$log_path" 2>&1
echo 1 - example output of command
echo 2 - script feedback/status | tee "/dev/fd/$term"
echo 3 - example output of command
echo 4 - script feedback/status | tee "/dev/fd/$term"
echo 5 - example output of command

CodePudding user response:

Based on the other answer, you can further define echo2 function :

echo2(){
    echo "$@" | tee "/dev/fd/$term"
}

exec {term}>&1 >"$log_path" 2>&1

echo  1 - example output of command
echo2 2 - script feedback/status
echo  3 - example output of command
echo2 4 - script feedback/status
echo  5 - example output of command
  • Related