基于这三个问题:
...我已将下面包含的脚本testlogredir.sh
放在一起。它要做的是:运行三个命令,其stdout和stderr输出将记录到终端和日志文件;然后运行另外两个命令,其stdout和stderr输出仅发送到终端。实际上,它启动并停止将脚本的终端输出重定向到logfile,同时保留终端输出。
有趣的是,如果我在停止日志到文件后使用sleep
,一切都按预期工作:
$ bash testlogredir.sh 1 y
--- testlogredir.sh METHOD=1 DOSLEEP=y ---
aaa
bbb
ccc
ddd
eee
$ cat test.log
aaa
bbb
ccc
...并且运行bash testlogredir.sh 2 y
也获得了相同的结果。
有趣的是,如果我不使用睡眠(使用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
...值得注意的是,首先将 last “ddd
”和“eee
”输出到终端;然后出现提示,然后输出第一个“aaa
”,“bbb
”,“ccc
” - 整个过程(b)锁定;所以我必须按Ctrl-C(^ C)退出它。但是,日志文件确实具有预期的内容。
我推测在没有睡眠的情况下,bash解释器在脚本中运行得非常快,它设法回应“最后”两个“ddd
”和“eee
”首先 - 然后只有tee
输出它存储的内容(请注意,这不是由于tee
的缓冲行为,因为我也尝试过stdbuf
得到相同的结果),显然是tee
进行阻止。因此,在某种程度上,添加sleep
会使bash脚本与tee
(sub?)进程“同步”。
显然,我希望命令输出按顺序显示 - 而sleep
本身并不会让我感到烦恼,因为我可以将它设置为sleep 0.1
而几乎没有注意到它。但我不得不问 - 这是从tee
脚本中进行这种启动/停止“bash
”重定向的正确方法吗?换句话说 - 有没有使用sleep
来实现这种“同步”的替代方案,可以这么说呢?
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
答案 0 :(得分:1)
您可以使用大括号通过管道将命令重定向到tee
#!/bin/bash
# to terminal and logfile.log
{
echo "aaa"
echo "bbb"
echo "ccc"
} 2>&1 | tee logfile.log
# only terminal
echo "ddd"
echo "eee"