代码之家  ›  专栏  ›  技术社区  ›  Vitaly Kushner

将标准输出的副本从bash脚本本身重定向到日志文件

  •  217
  • Vitaly Kushner  · 技术社区  · 14 年前

    我知道怎么做 重定向标准输出 到文件:

    exec > foo.log
    echo test
    

    现在我想 将输出重定向到日志文件并将其保持在标准输出上

    i、 e.可以在脚本之外完成一些琐碎的工作:

    script | tee foo.log
    

    但是我想在脚本中声明它

    我试过了

    exec | tee foo.log
    

    但没用。

    9 回复  |  直到 5 年前
        1
  •  301
  •   gsamaras    7 年前
    #!/usr/bin/env bash
    
    # Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
    exec > >(tee -i logfile.txt)
    
    # Without this, only stdout would be captured - i.e. your
    # log file would not contain any error messages.
    # SEE (and upvote) the answer by Adam Spiers, which keeps STDERR
    # as a separate stream - I did not want to steal from him by simply
    # adding his answer to mine.
    exec 2>&1
    
    echo "foo"
    echo "bar" >&2
    

    请注意,这是 bash ,不是 sh . 如果用 sh myscript.sh ,您将得到沿 syntax error near unexpected token '>' .

    tee -i


    ls 例如,使用颜色和列化输出)会将上述构造检测为它们输出到管道的意思。

    有强制着色/列化的选项(例如。 ls -C --color=always 可读。

        2
  •  178
  •   Adam Spiers    8 年前

    ./script.sh >/dev/null
    

    不会输出 bar 到终端,只到日志文件,和

    ./script.sh 2>/dev/null
    

    foo 酒吧 到终点站。显然不是这样 正常用户可能期望的行为。这可能是 通过使用两个单独的tee进程(都附加到同一个进程)进行修复

    #!/bin/bash
    
    # See (and upvote) the comment by JamesThomasMoon1979 
    # explaining the use of the -i option to tee.
    exec >  >(tee -ia foo.log)
    exec 2> >(tee -ia foo.log >&2)
    
    echo "foo"
    echo "bar" >&2
    

    (请注意,上面的内容最初并不会截断日志文件-如果您希望这种行为,应该添加

    >foo.log
    

    到脚本顶部。)

    POSIX.1-2008 specification of tee(1) foo.log ; 但是,这也可能发生在终端上,因此日志文件将忠实地反映 能够

        3
  •  28
  •   jbarlow    6 年前

    busybox、macosbash和非bash shell的解决方案

    exec > >(tee log.txt) exec >$PIPE 正确地说,尝试创建一个与命名管道同名的普通文件会失败并挂起。

    rm $PIPE

    注意,$*的使用不一定是安全的。

    #!/bin/sh
    
    if [ "$SELF_LOGGING" != "1" ]
    then
        # The parent process will enter this branch and set up logging
    
        # Create a named piped for logging the child's output
        PIPE=tmp.fifo
        mkfifo $PIPE
    
        # Launch the child process with stdout redirected to the named pipe
        SELF_LOGGING=1 sh $0 $* >$PIPE &
    
        # Save PID of child process
        PID=$!
    
        # Launch tee in a separate process
        tee logfile <$PIPE &
    
        # Unlink $PIPE because the parent process no longer needs it
        rm $PIPE    
    
        # Wait for child process, which is running the rest of this script
        wait $PID
    
        # Return the error code from the child process
        exit $?
    fi
    
    # The rest of the script goes here
    
        4
  •  19
  •   WReach    14 年前

    (
    echo start
    ls -l
    echo end
    ) | tee foo.log
    
        5
  •  15
  •   Tobu    10 年前

    将bash脚本记录到syslog的简单方法。脚本输出可以通过 /var/log/syslog

    在顶部添加以下行:

    exec &> >(logger -t myscript -s)
    

    或者,将日志发送到单独的文件:

    exec &> >(ts |tee -a /tmp/myscript.output >&2 )
    

    这需要 moreutils (对于 ts 命令,添加时间戳)。

        6
  •  10
  •   fgunger    13 年前

    # Logging setup
    logfile=mylogfile
    mkfifo ${logfile}.pipe
    tee < ${logfile}.pipe $logfile &
    exec &> ${logfile}.pipe
    rm ${logfile}.pipe
    
    # Rest of my script
    

    这使得脚本的输出从进程开始,通过管道进入“tee”的子后台进程,该进程将所有内容记录到光盘和脚本的原始stdout。

    请注意“exec&>'重定向stdout和stderr,我们可以根据需要分别重定向它们,或者更改为“exec>”如果我们只是想喝点酒。

        7
  •  2
  •   Dennis Williamson    14 年前

    Bash 4有一个 coproc

        8
  •  1
  •   Oliver    6 年前

    不能说我对任何基于exec的解决方案都很满意。我更喜欢直接使用tee,因此在请求时我使用tee调用脚本本身:

    # my script: 
    
    check_tee_output()
    {
        # copy (append) stdout and stderr to log file if TEE is unset or true
        if [[ -z $TEE || "$TEE" == true ]]; then 
            echo '-------------------------------------------' >> log.txt
            echo '***' $(date) $0 $@ >> log.txt
            TEE=false $0 $@ 2>&1 | tee --append log.txt
            exit $?
        fi 
    }
    
    check_tee_output $@
    
    rest of my script
    

    your_script.sh args           # tee 
    TEE=true your_script.sh args  # tee 
    TEE=false your_script.sh args # don't tee
    export TEE=false
    your_script.sh args           # tee
    

    你可以自定义它,例如,使tee=false为默认值,使tee保留日志文件,等等。我猜这个解决方案类似于jbarlow的,但更简单,也许我的有我还没有遇到的限制。

        9
  •  -1
  •   David Z    14 年前

    这两种方法都不是完美的解决方案,但以下是一些您可以尝试的方法:

    exec >foo.log
    tail -f foo.log &
    # rest of your script
    

    PIPE=tmp.fifo
    mkfifo $PIPE
    exec >$PIPE
    tee foo.log <$PIPE &
    # rest of your script
    rm $PIPE
    

    第二种方法是,如果脚本出了问题,就留下一个管道文件,这可能是问题,也可能不是问题(即,可能是问题) rm