Reputation: 38619
Based on these three questions:
... I have put together the script testlogredir.sh
, included below. What it's trying to do is: run three commands, whose stdout and stderr output will be logged both to terminal and log file; and then run two more commands, whose stdout and stderr output is only sent to terminal. In effect, it's starting and stopping redirection of the script's terminal output to logfile, while preserving the terminal output.
The interesting thing is, that if I use a sleep
after stopping the log-to-file, everything works as expected:
$ bash testlogredir.sh 1 y
--- testlogredir.sh METHOD=1 DOSLEEP=y ---
aaa
bbb
ccc
ddd
eee
$ cat test.log
aaa
bbb
ccc
... and the same result is also obtained running bash testlogredir.sh 2 y
.
Interestingly, if I do not use the sleep (same output will also be obtained with bash testlogredir.sh 1
):
$ bash testlogredir.sh 2
--- testlogredir.sh METHOD=2 DOSLEEP= ---
ddd
eee
$ aaa
bbb
ccc
^C
$ cat test.log
aaa
bbb
ccc
... it is notable that first the last "ddd
" and "eee
" are output to terminal; then prompt appears then the first "aaa
", "bbb
", "ccc
" are output - and the process as a whole (b)locks; so I have to press Ctrl-C (^C) to exit it. The logfile, however, does have the contents as expected.
I speculate that in the no sleep case, the bash interpreter ran so quickly through the script, that it managed to echo the "last" two "ddd
" and "eee
" first - and only then does tee
output what it has stored (note that this is not due to buffering behavior of tee
, as I've tried it also with stdbuf
getting the same results), and apparently it is the tee
that does the blocking. Thus, adding the sleep
, makes the bash script "synchronize" with the tee
(sub?)process, in a way.
Obviously, I'd like the command outputs to be shown in sequential order - and the sleep
itself doesn't bother me that much, as I can set it to sleep 0.1
and barely notice it. But I have to ask - is this the right way to do this kind of start/stop "tee
" redirection from within a bash
script? In other words - is there an alternative to using sleep
to achieve this kind of "synchronization", so to speak?
testlogredir.sh
#!/usr/bin/env bash
# testlogredir.sh
# defaults:
METHOD="1" # or "2"
DOSLEEP="" # or "y"
if [ "$1" ] ; then
METHOD="$1" ;
fi
if [ "$2" ] ; then
DOSLEEP="$2" ;
fi
# this should be echoed only to terminal
echo " --- $0 METHOD=$METHOD DOSLEEP=$DOSLEEP ---"
# silent remove of test.log
rm -f test.log
if [ $METHOD == "1" ] ; then
# Redirect 3 into (use fd3 as reference to) /dev/stdout
exec 3> /dev/stdout
# Redirect 4 into (use fd4 as reference to) /dev/stderr
exec 4> /dev/stderr
fi
if [ $METHOD == "2" ] ; then
# backup the original filedescriptors, first
# stdout (1) into fd6; stderr (2) into fd7
exec 6<&1
exec 7<&2
fi
# Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
#~ exec > >(stdbuf -i0 -o0 tee test.log)
exec > >(tee test.log)
# Redirect stderr (2) into stdout (1)
exec 2>&1
# these should be echoed both to terminal and log
echo "aaa"
echo "bbb"
echo "ccc" >&2
if [ $METHOD == "1" ] ; then
# close current stdout, stderr
exec 1>&-
exec 2>&-
# Redirect stdout (1) and stderr (2)
exec 1>&3
exec 2>&1
fi
if [ $METHOD == "2" ] ; then
# close and restore backup; both stdout and stderr
exec 1<&6 6<&-
exec 2<&7 2<&-
# Redirect stderr (2) into stdout (1)
exec 2>&1
fi
if [ "$DOSLEEP" ] ; then
sleep 1 ;
fi
# these should be echoed only to terminal
echo "ddd"
echo "eee" >&2
exit
Upvotes: 0
Views: 5577
Reputation: 160
You can use the braces to redirect commands to tee
through a pipe
#!/bin/bash
# to terminal and logfile.log
{
echo "aaa"
echo "bbb"
echo "ccc"
} 2>&1 | tee logfile.log
# only terminal
echo "ddd"
echo "eee"
Upvotes: 2