Linux 将“tee”与管道一起使用时,如何将 stderr 写入文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/692000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:09:43  来源:igfitidea点击:

How do I write stderr to a file while using "tee" with a pipe?

linuxbashunix

提问by jparanich

I know how to use teeto write the output (STDOUT) of aaa.shto bbb.out, while still displaying it in the terminal:

我知道如何使用tee写输出(STDOUT中)aaa.shbbb.out,同时还在终端显示它:

./aaa.sh | tee bbb.out

How would I now also write STDERRto a file named ccc.out, while still having it displayed?

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

采纳答案by lhunath

I'm assuming you want to still see STDERR and STDOUT on the terminal. You could go for Josh Kelley's answer, but I find keeping a tailaround in the background which outputs your log file very hackish and cludgy. Notice how you need to keep an exra FD and do cleanup afterward by killing it and technically should be doing that in a trap '...' EXIT.

我假设您仍然希望在终端上看到 STDERR 和 STDOUT。您可以选择 Josh Kelley 的答案,但我发现tail在后台保留一个输出您的日志文件非常笨拙和笨拙。请注意您需要如何保留一个 exra FD 并在之后通过杀死它来进行清理,并且技术上应该在trap '...' EXIT.

There is a better way to do this, and you've already discovered it: tee.

有一种更好的方法可以做到这一点,您已经发现了它:tee.

Only, instead of just using it for your stdout, have a tee for stdout and one for stderr. How will you accomplish this? Process substitution and file redirection:

只是,而不是仅仅将它用于您的标准输出,有一个用于标准输出的 T 恤和一个用于标准错误的 T 恤。你将如何做到这一点?进程替换和文件重定向:

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

Let's split it up and explain:

让我们把它分开并解释:

> >(..)

>(...)(process substitution) creates a FIFO and lets teelisten on it. Then, it uses >(file redirection) to redirect the STDOUT of commandto the FIFO that your first teeis listening on.

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

Same thing for the second:

第二个同样的事情:

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

We use process substitution again to make a teeprocess that reads from STDIN and dumps it into stderr.log. teeoutputs its input back on STDOUT, but since its input is our STDERR, we want to redirect tee's STDOUT to our STDERR again. Then we use file redirection to redirect command's STDERR to the FIFO's input (tee's STDIN).

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

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

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

Process substitution is one of those really lovely things you get as a bonus of choosing bashas your shell as opposed to sh(POSIX or Bourne).

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



In sh, you'd have to do things manually:

在 中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"

回答by Josh Kelley

To redirect stderr to a file, display stdout to screen, and also save stdout to a file:

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

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

EDIT: To display both stderr and stdout to screen and also save both to a file, you can use bash's I/O redirection:

编辑:要在屏幕上同时显示 stderr 和 stdout 并将它们保存到文件中,您可以使用 bash 的I/O 重定向

#!/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

回答by ChristopheD

If using bash:

如果使用 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

Credit (not answering from the top of my head) goes here: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html

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

回答by user542833

why not simply:

为什么不简单:

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

This simply redirects stderrto stdout, so tee echoes both to log and to screen. Maybe I'm missing something, because some of the other solutions seem really complicated.

这只是重定向stderrstdout,因此 tee 回显到日志和屏幕。也许我遗漏了一些东西,因为其他一些解决方案看起来非常复杂。

Note:Since bash version 4 you may use |&as an abbreviation for 2>&1 |:

注意:从 bash 版本 4 开始,您可以使用|&以下缩写2>&1 |

./aaa.sh |& tee -a log

回答by Gilles 'SO- stop being evil'

In other words, you want to pipe stdout into one filter (tee bbb.out) and stderr into another filter (tee ccc.out). There is no standard way to pipe anything other than stdout into another command, but you can work around that by juggling file descriptors.

换句话说,您希望通过管道将 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

See also How to grep standard error stream (stderr)?and When would you use an additional file descriptor?

另请参阅如何 grep 标准错误流 (stderr)?你什么时候会使用一个额外的文件描述符?

In bash (and ksh and zsh), but not in other POSIX shells such as dash, you can use process substitution:

在 bash(以及 ksh 和 zsh)中,但不是在其他 POSIX shell 中,例如 dash,您可以使用进程替换

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

Beware that in bash, this command returns as soon as ./aaa.shfinishes, even if the teecommands are still executed (ksh and zsh do wait for the subprocesses). This may be a problem if you do something like ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. In that case, use file descriptor juggling or ksh/zsh instead.

请注意,在 bash 中,./aaa.sh即使tee命令仍在执行(ksh 和 zsh 确实等待子进程),该命令也会在完成后立即返回。如果您执行类似./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out. 在这种情况下,请改用文件描述符杂耍或 ksh/zsh。

回答by Anthony

This may be useful for people finding this via google. Simply uncomment the example you want to try out. Of course, feel free to rename the output files.

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

#!/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 
cmd > log 2>&1

回答by haridsv

In my case, a script was running command while redirecting both stdout and stderr to a file, something like:

就我而言,脚本在将 stdout 和 stderr 重定向到文件时正在运行命令,例如:

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

I needed to update it such that when there is a failure, take some actions based on the error messages. I could of course remove the dup 2>&1and capture the stderr from the script, but then the error messages won't go into the log file for reference. While the accepted answer from @lhunath is supposed to do the same, it redirects stdoutand stderrto different files, which is not what I want, but it helped me to come up with the exact solution that I need:

我需要更新它,以便在出现故障时,根据错误消息采取一些措施。我当然可以删除 dup2>&1并从脚本中捕获 stderr,但是错误消息不会进入日志文件以供参考。虽然从@lhunath接受的答案是应该做同样的,它重定向stdoutstderr不同的文件,这是不是我想要的,但它让我拿出精确解,我需要:

# 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>&-

With the above, log will have a copy of both stdoutand stderrand I can capture stderrfrom my script without having to worry about stdout.

通过上述,日志将同时拥有的副本stdout,并stderr和我可以捕捉stderr从我的脚本,而不必操心stdout

回答by shuva

The following will work for KornShell(ksh) where the process substitution is not available,

以下将适用于进程替换不可用的 KornShell(ksh),

./cmd 1>&1 2>&2 1>out_file 2>err_file

The real trick here, is the sequence of the 2>&1 1>&3which in our case redirects the stderrto stdoutand redirects the stdoutto descriptor 3. At this point the stderrand stdoutare not combined yet.

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

In effect, the stderr(as stdin) is passed to teewhere it logs to stderr.logand also redirects to descriptor 3.

实际上,stderr(as stdin) 被传递到tee它登录的地方,stderr.log并重定向到描述符 3。

And descriptor 3is logging it to combined.logall the time. So the combined.logcontains both stdoutand stderr.

描述符3一直在记录它combined.log。所以combined.log包含stdoutstderr

回答by Arminius

If you're using zsh, you can use multiple redirections, so you don't even need tee:

如果您使用的是zsh,则可以使用多个重定向,因此您甚至不需要tee

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

Here you're simply redirecting each stream to itself andthe target file.

在这里,您只需将每个流重定向到自身目标文件。



Full example

完整示例

##代码##

Note that this requires the MULTIOSoption to be set (which is the default).

请注意,这需要设置MULTIOS选项(这是默认设置)。

MULTIOS

Perform implicit tees or cats when multiple redirections are attempted (see Redirection).

MULTIOS

尝试多次重定向时执行隐式tees 或cats(请参阅重定向)。