2013-08-19 101 views
0

基於這三個問題中的文件:啓動和停止記錄終端輸出從bash腳本

...我有放在一起腳本testlogredir.sh,包含在下面。它試圖做的是:運行三個命令,其stdout和stderr輸出將被記錄到終端和日誌文件;然後再運行兩個命令,其stdout和stderr輸出僅發送給終端。實際上,它將啓動和停止將腳本的終端輸出重定向到日誌文件,同時保留終端輸出。

有趣的是,如果我用一個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 

...值得注意的是,第一最後ddd」和「eee」輸出到終端;然後提示然後出現的第一個「aaa」,「bbb」,「ccc」是輸出 - 並且該過程作爲一個整體(b)中的鎖;所以我必須按Ctrl-C(^ C)才能退出它。但是,日誌文件確實具有預期的內容。

我推測,在沒有睡眠的情況下,在bash解釋器通過腳本跑這麼快,它成功地呼應了「最後」二「ddd」和「eee第一 - 然後才做tee輸出它已經存儲了(注意,這是不是由於tee緩衝行爲,因爲我已經stdbuf得到相同的結果試了一下還),顯然它是不會阻塞的tee。因此,添加sleep,以某種方式使bash腳本與tee(sub?)進程「同步」。

很顯然,我希望命令輸出按順序顯示 - 而sleep本身並沒有打擾我很多,因爲我可以將它設置爲sleep 0.1,但幾乎沒有注意到它。但我要問 - 這是做這種開始以正確的方式從bash腳本中/停止「tee」重定向?換句話說 - 有沒有其他方法可以使用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 

回答

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" 
+0

謝謝,@vegatripy - 我想,以避免在大括號的方法,因爲這三個命令可以是一個獨立對於100個命令,我不想追蹤那裏的額外大括號。我更喜歡「睡眠」命令的替代方案,如果存在的話......乾杯! – sdaau