首页 文章

在管道上使用“tee”时如何将stderr写入文件?

提问于
浏览
444

我知道如何使用 teeaaa.sh 的输出( STDOUT )写入 bbb.out ,同时仍然在终端中显示它:

./aaa.sh | tee bbb.out

我现在如何将 STDERR 写入名为 ccc.out 的文件中,同时仍然显示它?

8 回答

  • 2

    这可能对通过谷歌找到这个的人有用 . 只需取消注释您想要尝试的示例即可 . 当然,随意重命名输出文件 .

    #!/bin/bash
    
    STATUSFILE=x.out
    LOGFILE=x.log
    
    ### All output to screen
    ### Do nothing, this is the default
    
    
    ### All Output to one file, nothing to the screen
    #exec > ${LOGFILE} 2>&1
    
    
    ### All output to one file and all output to the screen
    #exec > >(tee ${LOGFILE}) 2>&1
    
    
    ### All output to one file, STDOUT to the screen
    #exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)
    
    
    ### All output to one file, STDERR to the screen
    ### Note you need both of these lines for this to work
    #exec 3>&1
    #exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)
    
    
    ### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
    #exec > ${STATUSFILE} 2>${LOGFILE}
    
    
    ### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
    #exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)
    
    
    ### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
    #exec > >(tee ${STATUSFILE}) 2>${LOGFILE}
    
    
    ### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
    #exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)
    
    
    echo "This is a test"
    ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
    ls -l ${0}
    
  • 14

    为什么不简单:

    ./aaa.sh 2>&1 | tee -a log
    

    这只是简单地将 stderr 重定向到 stdout ,因此tee回应记录和屏幕 . 也许我错过了一些东西,因为其他一些解决方案似乎很复杂 .

    Note: 从bash版本4开始,您可以使用 |& 作为 2>&1 | 的缩写:

    ./aaa.sh |& tee -a log
    
  • 20

    如果使用bash:

    # Redirect standard out and standard error separately
    % cmd >stdout-redirect 2>stderr-redirect
    
    # Redirect standard error and out together
    % cmd >stdout-redirect 2>&1
    
    # Merge standard error with standard out and pipe
    % cmd 2>&1 |cmd2
    

    信用(不是从我的头脑回答)到这里:http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html

  • 46

    我是_513165的答案,但我发现在后台保留了一个 tail ,输出你的日志文件非常hackish和cludgy . 注意你需要保留一个exra FD并在之后通过杀死它进行清理,技术上应该在_513167中进行清理 .

    有一种更好的方法,你已经发现了它: tee .

    只是,而不是仅仅为stdout使用它,有stdout的发球台和stderr的发球台 . 你将如何实现这一目标?进程替换和文件重定向:

    command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
    

    让我们分开并解释一下:

    > >(..)
    

    >(...) (进程替换)创建一个FIFO并让 tee 监听它 . 然后,它使用 > (文件重定向)将 command 的STDOUT重定向到第一个 tee 正在侦听的FIFO .

    同样的事情是第二个:

    2> >(tee -a stderr.log >&2)
    

    我们再次使用进程替换来创建一个从STDIN读取的 tee 进程并将其转储到 stderr.log 中 . tee 在STDOUT上输出其输入,但由于它的输入是我们的STDERR,我们想要再次将 tee 的STDOUT重定向到我们的STDERR . 然后我们使用文件重定向来重定向 command 's STDERR to the FIFO' s输入( tee 的STDIN) .

    http://mywiki.wooledge.org/BashGuide/InputAndOutput

    进程替换是你选择 bash 作为你的shell而不是 sh (POSIX或Bourne)的额外奖励之一 .


    sh 中,您必须手动执行操作:

    out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
    mkfifo "$out" "$err"
    trap 'rm "$out" "$err"' EXIT
    tee -a stdout.log < "$out" &
    tee -a stderr.log < "$err" >&2 &
    command >"$out" 2>"$err"
    
  • 2

    以下内容适用于KornShell(ksh),其中没有进程替换,

    # create a combined(stdin and stdout) collector
    exec 3 <> combined.log
    
    # stream stderr instead of stdout to tee, while draining all stdout to the collector
    ./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3
    
    # cleanup collector
    exec 3>&-
    

    这里的真正技巧是 2>&1 1>&3 的序列,在我们的例子中,它将 stderr 重定向到 stdout 并将 stdout 重定向到描述符 3 . 此时 stderrstdout 尚未合并 .

    实际上, stderr (如 stdin )被传递到 tee ,在那里它记录到 stderr.log 并且还重定向到描述符3 .

    描述符 3 一直将其记录到 combined.log . 所以 combined.log 包含 stdoutstderr .

  • 657

    换句话说,您希望将stdout传递到一个过滤器( tee bbb.out )并将stderr传递到另一个过滤器( tee ccc.out ) . 没有标准的方法将除stdout以外的任何东西传递到另一个命令,但你可以通过杂乱文件描述符来解决这个问题 .

    { { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2
    

    另见How to grep standard error stream (stderr)?When would you use an additional file descriptor?

    在bash(和ksh和zsh)中,但在其他POSIX shell(如dash)中没有,你可以使用process substitution

    ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
    

    请注意,在bash中,即使 tee 命令仍在执行(ksh和zsh等待子进程),此命令也会在 ./aaa.sh 完成后立即返回 . 如果您执行类似 ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out 的操作,这可能会出现问题 . 在这种情况下,请改用文件描述符juggling或ksh / zsh .

  • 543

    在我的例子中,脚本运行命令同时将stdout和stderr重定向到文件,如:

    cmd > log 2>&1
    

    我需要更新它,以便在出现故障时,根据错误消息采取一些操作 . 我当然可以删除dup 2>&1 并从脚本中捕获stderr,但随后错误消息将不会进入日志文件以供参考 . 虽然@lhunath接受的答案应该是这样做的,但它会将 stdoutstderr 重定向到不同的文件,这不是我想要的,但它帮助我提出了我需要的确切解决方案:

    (cmd 2> >(tee /dev/stderr)) > log
    

    有了上面的内容,日志将包含 stdoutstderr 的副本,我可以从我的脚本中捕获 stderr ,而不必担心 stdout .

  • 11

    要将stderr重定向到文件,请将stdout显示到屏幕,并将stdout保存到文件:

    ./aaa.sh 2>ccc.out | tee ./bbb.out
    

    EDIT :要将stderr和stdout同时显示到屏幕并同时保存到文件中,可以使用bash的I/O redirection

    #!/bin/bash
    
    # Create a new file descriptor 4, pointed at the file
    # which will receive stderr.
    exec 4<>ccc.out
    
    # Also print the contents of this file to screen.
    tail -f ccc.out &
    
    # Run the command; tee stdout as normal, and send stderr
    # to our file descriptor 4.
    ./aaa.sh 2>&4 | tee bbb.out
    
    # Clean up: Close file descriptor 4 and kill tail -f.
    exec 4>&-
    kill %1
    

相关问题