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