#!/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}
# 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
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) .
进程替换是你选择 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>&-
#!/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
8 回答
这可能对通过谷歌找到这个的人有用 . 只需取消注释您想要尝试的示例即可 . 当然,随意重命名输出文件 .
为什么不简单:
这只是简单地将
stderr
重定向到stdout
,因此tee回应记录和屏幕 . 也许我错过了一些东西,因为其他一些解决方案似乎很复杂 .Note: 从bash版本4开始,您可以使用
|&
作为2>&1 |
的缩写:如果使用bash:
信用(不是从我的头脑回答)到这里:http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
我是_513165的答案,但我发现在后台保留了一个
tail
,输出你的日志文件非常hackish和cludgy . 注意你需要保留一个exra FD并在之后通过杀死它进行清理,技术上应该在_513167中进行清理 .有一种更好的方法,你已经发现了它:
tee
.只是,而不是仅仅为stdout使用它,有stdout的发球台和stderr的发球台 . 你将如何实现这一目标?进程替换和文件重定向:
让我们分开并解释一下:
>(...)
(进程替换)创建一个FIFO并让tee
监听它 . 然后,它使用>
(文件重定向)将command
的STDOUT重定向到第一个tee
正在侦听的FIFO .同样的事情是第二个:
我们再次使用进程替换来创建一个从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
中,您必须手动执行操作:以下内容适用于KornShell(ksh),其中没有进程替换,
这里的真正技巧是
2>&1 1>&3
的序列,在我们的例子中,它将stderr
重定向到stdout
并将stdout
重定向到描述符3
. 此时stderr
和stdout
尚未合并 .实际上,
stderr
(如stdin
)被传递到tee
,在那里它记录到stderr.log
并且还重定向到描述符3 .描述符
3
一直将其记录到combined.log
. 所以combined.log
包含stdout
和stderr
.换句话说,您希望将stdout传递到一个过滤器(
tee bbb.out
)并将stderr传递到另一个过滤器(tee ccc.out
) . 没有标准的方法将除stdout以外的任何东西传递到另一个命令,但你可以通过杂乱文件描述符来解决这个问题 .另见How to grep standard error stream (stderr)?和When would you use an additional file descriptor?
在bash(和ksh和zsh)中,但在其他POSIX shell(如dash)中没有,你可以使用process substitution:
请注意,在bash中,即使
tee
命令仍在执行(ksh和zsh等待子进程),此命令也会在./aaa.sh
完成后立即返回 . 如果您执行类似./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
的操作,这可能会出现问题 . 在这种情况下,请改用文件描述符juggling或ksh / zsh .在我的例子中,脚本运行命令同时将stdout和stderr重定向到文件,如:
我需要更新它,以便在出现故障时,根据错误消息采取一些操作 . 我当然可以删除dup
2>&1
并从脚本中捕获stderr,但随后错误消息将不会进入日志文件以供参考 . 虽然@lhunath接受的答案应该是这样做的,但它会将stdout
和stderr
重定向到不同的文件,这不是我想要的,但它帮助我提出了我需要的确切解决方案:有了上面的内容,日志将包含
stdout
和stderr
的副本,我可以从我的脚本中捕获stderr
,而不必担心stdout
.要将stderr重定向到文件,请将stdout显示到屏幕,并将stdout保存到文件:
EDIT :要将stderr和stdout同时显示到屏幕并同时保存到文件中,可以使用bash的I/O redirection: