【问题标题】:Start and stop logging terminal output to file from within bash script从 bash 脚本中开始和停止将终端输出记录到文件
【发布时间】:2013-08-19 18:58:48
【问题描述】:

基于这三个问题:

...我整理了脚本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

...值得注意的是,首先lastddd”和“eee”输出到终端;然后出现提示,然后输出第一个“aaa”、“bbb”、“ccc” - 整个过程(b)锁定;所以我必须按 Ctrl-C (^C) 退出它。但是,日志文件确实具有预期的内容。

我推测在没有睡眠的情况下,bash 解释器在脚本中运行得如此之快,以至于它设法回显了“最后”两个“ddd”和“eeefirst - 然后tee 才输出它存储的内容(请注意,这不是由于tee 的缓冲行为,因为我也尝试过stdbuf 得到相同的结果),显然它是进行阻塞的tee。因此,添加sleep 会使bash 脚本与tee(子?)进程在某种程度上“同步”。

显然,我希望命令输出按顺序显示 - 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

【问题讨论】:

    标签: bash


    【解决方案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"
    

    【讨论】:

    • 谢谢,@vegatripy - 我想避免使用大括号方法,因为这三个命令可能是 100 条命令的替代,我不想在那里跟踪额外的大括号.我对sleep 命令的替代品更感兴趣,如果存在的话......干杯!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 2019-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多