当使用带管道的“tee”时,如何将stderr写入文件?

时间:2022-03-06 09:47:41

I have below a command which will print the output of aaa.sh to the screen while also writing stdout to bbb.out; however I would also like to write stderr to a file named ccc.out. Any suggestions on how to modify the piece below?

我有一个命令,它将把aaa.sh的输出打印到屏幕上,同时也将stdout写入bbb.out;但是,我也想将stderr写入一个名为ccc.out的文件中。关于如何修改下面的部分有什么建议吗?

./aaa.sh | tee ./bbb.out

Update: stdout and stderr should still both be printed to the screen, regardless.

更新:无论如何,stdout和stderr都应该打印到屏幕上。

8 个解决方案

#1


609  

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 tail around 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的答案,但是我发现在后台留个尾巴,这样就会输出你的日志文件,非常陈腐和杂乱。注意你需要如何保持一个exra FD,然后通过杀死它来做清理,从技术上来说,应该在陷阱里这样做……“退出。

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:

只有,不要只在stdout中使用它,而要在stdout中使用一个tee,在stderr中使用一个tee。你将如何做到这一点?过程替换和文件重定向:

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 tee listen on it. Then, it uses > (file redirection) to redirect the STDOUT of command to the FIFO that your first tee is listening on.

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

Same thing for the second:

第二个也是一样的:

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

We use process substitution again to make a tee process that reads from STDIN and dumps it into stderr.log. tee outputs 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).

我们再次使用进程替换来创建一个从STDIN读取并将其转储到stderr.log的tee进程。tee将其输入输出回STDOUT,但是由于它的输入是我们的STDERR,所以我们想再次将tee的STDOUT重定向到STDERR。然后我们使用文件重定向将命令的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 bash as 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"

#2


495  

why not simply:

为什么不简单:

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

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

这简单地将stderr重定向到stdout,因此tee响应了日志和屏幕。也许我遗漏了一些东西,因为一些其他的解看起来很复杂。

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

注意:由于bash版本4,您可以使用|&作为2>和1 |的缩写:

./aaa.sh |& tee -a log

#3


41  

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 ${0}

#4


19  

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

#5


13  

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.sh finishes, even if the tee commands 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等待子进程)。如果你做类似的事情,这可能是个问题。> >(tee bbb.out) 2> >(tee ccc.out);process_logs bbb。ccc.out。在这种情况下,使用文件描述符杂耍或ksh/zsh代替。

#6


11  

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

#7


2  

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

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

cmd > log 2>&1

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>&1 and 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 stdout and stderr to different files, which is not what I want, but it helped me to come up with the exact solution that I need:

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

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

With the above, log will have a copy of both stdout and stderr and I can capture stderr from my script without having to worry about stdout.

有了上面的内容,log将同时拥有stdout和stderr的副本,我可以从脚本中捕获stderr,而不必担心stdout。

#8


2  

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

以下代码将适用于没有流程替代的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>&-

The real trick here, is the sequence of the 2>&1 1>&3 which in our case redirects the stderr to stdout and redirects the stdout to descriptor 3. At this point the stderr and stdout are not combined yet.

这里真正的技巧是2>和1>和3的序列,在我们的例子中,它将stderr重定向到stdout,并将stdout重定向到描述符3。此时stderr和stdout还没有合并。

In effect, the stderr(as stdin) is passed to tee where it logs to stderr.log and also redirects to descriptor 3.

实际上,stderr(作为stdin)被传递到tee,在那里它记录到stderr。并重定向到描述符3。

And descriptor 3 is logging it to combined.log all the time. So the combined.log contains both stdout and stderr.

描述符3将它记录到一起。日志。因此,总和。日志包含stdout和stderr。

#1


609  

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 tail around 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的答案,但是我发现在后台留个尾巴,这样就会输出你的日志文件,非常陈腐和杂乱。注意你需要如何保持一个exra FD,然后通过杀死它来做清理,从技术上来说,应该在陷阱里这样做……“退出。

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:

只有,不要只在stdout中使用它,而要在stdout中使用一个tee,在stderr中使用一个tee。你将如何做到这一点?过程替换和文件重定向:

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 tee listen on it. Then, it uses > (file redirection) to redirect the STDOUT of command to the FIFO that your first tee is listening on.

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

Same thing for the second:

第二个也是一样的:

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

We use process substitution again to make a tee process that reads from STDIN and dumps it into stderr.log. tee outputs 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).

我们再次使用进程替换来创建一个从STDIN读取并将其转储到stderr.log的tee进程。tee将其输入输出回STDOUT,但是由于它的输入是我们的STDERR,所以我们想再次将tee的STDOUT重定向到STDERR。然后我们使用文件重定向将命令的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 bash as 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"

#2


495  

why not simply:

为什么不简单:

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

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

这简单地将stderr重定向到stdout,因此tee响应了日志和屏幕。也许我遗漏了一些东西,因为一些其他的解看起来很复杂。

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

注意:由于bash版本4,您可以使用|&作为2>和1 |的缩写:

./aaa.sh |& tee -a log

#3


41  

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 ${0}

#4


19  

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

#5


13  

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.sh finishes, even if the tee commands 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等待子进程)。如果你做类似的事情,这可能是个问题。> >(tee bbb.out) 2> >(tee ccc.out);process_logs bbb。ccc.out。在这种情况下,使用文件描述符杂耍或ksh/zsh代替。

#6


11  

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

#7


2  

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

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

cmd > log 2>&1

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>&1 and 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 stdout and stderr to different files, which is not what I want, but it helped me to come up with the exact solution that I need:

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

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

With the above, log will have a copy of both stdout and stderr and I can capture stderr from my script without having to worry about stdout.

有了上面的内容,log将同时拥有stdout和stderr的副本,我可以从脚本中捕获stderr,而不必担心stdout。

#8


2  

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

以下代码将适用于没有流程替代的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>&-

The real trick here, is the sequence of the 2>&1 1>&3 which in our case redirects the stderr to stdout and redirects the stdout to descriptor 3. At this point the stderr and stdout are not combined yet.

这里真正的技巧是2>和1>和3的序列,在我们的例子中,它将stderr重定向到stdout,并将stdout重定向到描述符3。此时stderr和stdout还没有合并。

In effect, the stderr(as stdin) is passed to tee where it logs to stderr.log and also redirects to descriptor 3.

实际上,stderr(作为stdin)被传递到tee,在那里它记录到stderr。并重定向到描述符3。

And descriptor 3 is logging it to combined.log all the time. So the combined.log contains both stdout and stderr.

描述符3将它记录到一起。日志。因此,总和。日志包含stdout和stderr。