Answers:
我假设您仍然想在终端上看到STDERR和STDOUT。您可以寻求Josh Kelley的答案,但我发现tail
在后台保留一些内容,这会使您的日志文件非常骇人且笨拙。请注意,您需要如何保留exra FD并随后通过杀死它来进行清理,从技术上讲,应在Windows XP中执行此操作trap '...' EXIT
。
有一种更好的方法可以做到这一点,并且您已经发现了:tee
。
只是,不仅仅是为标准输出使用它,还有为标准输出提供一个tee,为stderr提供一个。您将如何实现?进程替换和文件重定向:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
让我们对其进行解释:
> >(..)
>(...)
(进程替换)创建FIFO并对其进行tee
侦听。然后,它使用>
(文件重定向)将STDOUT重定向command
到第一个tee
监听的FIFO 。
第二件事:
2> >(tee -a stderr.log >&2)
我们再次使用流程替换来创建一个tee
从STDIN读取并将其转储到的进程stderr.log
。 tee
将其输入返回到STDOUT上,但是由于其输入是我们的STDERR,因此我们想再次将tee
STDOUT 重定向到我们的STDERR。然后,我们使用文件重定向将command
的STDERR 重定向到FIFO的输入(tee
的STDIN)。
参见http://mywiki.wooledge.org/BashGuide/InputAndOutput
进程替换是您获得的那些非常可爱的东西之一,可以选择bash
作为您的外壳而不是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"
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
可以,但是等待输入。发生这种情况的原因很简单吗?
/bin/bash 2> err
和/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
为什么不简单地:
./aaa.sh 2>&1 | tee -a log
这只是重定向stderr
到stdout
,因此tee会回显日志和屏幕。也许我错过了一些东西,因为其他一些解决方案似乎真的很复杂。
注意:从bash版本4开始,您可以使用|&
以下缩写2>&1 |
:
./aaa.sh |& tee -a log
./aaa.sh |& tee aaa.log
可以使用bash。
set -o pipefail
,;
或者&&
我没记错的话,您可以保留退出状态。
这对于通过Google找到此信息的人可能有用。只需取消注释您要尝试的示例。当然,可以随意重命名输出文件。
#!/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}
exec >
意味着将文件描述符的目标移动到某个目标。默认值为1,因此exec > /dev/null
从此会话开始,将stdout的输出移至/ dev / null。可以通过查看此会话的当前文件描述符ls -l /dev/fd/
。尝试一下!然后查看发出命令时发生的情况。exec 2>/tmp/stderr.log.
此外,这exec 3>&1
意味着创建一个编号为3的新文件描述符,并将其重定向到文件描述符1的目标。在此示例中,目标是发出命令时的屏幕。
要将stderr重定向到文件,请在屏幕上显示stdout,并将stdout保存到文件:
./aaa.sh 2> ccc.out | 开球./bbb.out
编辑:要在屏幕上同时显示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
换句话说,您希望将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
另请参见如何grep标准错误流(stderr)?以及何时使用附加的文件描述符?
在bash(以及ksh和zsh)中,但在其他POSIX shell(如破折号)中则没有,可以使用进程替换:
./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
注意,在bash中,./aaa.sh
即使tee
命令仍在执行,该命令也会在完成后立即返回(ksh和zsh确实会等待子进程)。如果您做类似的事情,这可能是一个问题./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
。在这种情况下,请改用文件描述符杂耍或ksh / zsh。
sh
,对于无法使用进程替代的cron作业有用。
如果使用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
以我为例,脚本在将stdout和stderr都重定向到文件时正在运行命令,例如:
cmd > log 2>&1
我需要对其进行更新,以便在出现故障时根据错误消息采取一些措施。我当然可以2>&1
从脚本中删除dup 并捕获stderr,但是错误消息不会进入日志文件以供参考。虽然从@lhunath接受的答案是应该做同样的,它重定向stdout
和stderr
不同的文件,这是不是我想要的,但它让我拿出精确解,我需要:
(cmd 2> >(tee /dev/stderr)) > log
通过上述,日志将同时拥有的副本stdout
,并stderr
和我可以捕捉stderr
从我的脚本,而不必操心stdout
。
以下内容适用于无法进行进程替换的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
。此时stderr
和stdout
尚未合并。
实际上,stderr
(as stdin
)被传递到tee
它登录的位置,stderr.log
并且还重定向到描述符3。
描述符3
一直记录着它combined.log
。因此,combined.log
包含stdout
和stderr
。
如果您使用的是zsh,则可以使用多个重定向,因此您甚至不需要tee
:
./cmd 1>&1 2>&2 1>out_file 2>err_file
在这里,您只是将每个流重定向到其自身和目标文件。
完整的例子
% (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
请注意,这需要设置MULTIOS
选项(默认设置)。
MULTIOS
尝试进行多次重定向时,请执行
tee
或隐式scat
(请参阅Redirection)。