我如何获得STDOUT和STDERR去terminal和日志文件?

我有一个将由非技术用户交互式运行的脚本。 该脚本将状态更新写入STDOUT,以便用户可以确定脚本运行正常。

我希望STDOUT和STDERR都redirect到terminal(这样用户可以看到脚本正在工作以及是否有问题)。 我也希望这两个streamredirect到一个日志文件。

我在网上看到了一堆解决scheme。 有些不起作用,而另外一些则非常复杂。 我已经开发了一个可行的解决scheme(我将作为答案input),但是它很笨拙。

完美的解决scheme将是一行代码,可以合并到任何脚本的开始,这两个脚本都将stream发送到terminal和日志文件。

编辑:redirectSTDERR STDOUT和pipe道结果三通工程,但它取决于用户记住redirect和pipe道输出。 我希望logging是傻瓜式的和自动的(这就是为什么我想能够将解决schemeembedded到脚本本身中。)

使用“tee”redirect到文件和屏幕。 根据您使用的shell,您首先必须使用将stderrredirect到stdout

./a.out 2>&1 | tee output 

要么

 ./a.out |& tee output 

在csh中,有一个名为“脚本”的内置命令,它将捕获所有进入屏幕的文件。 你可以通过input“script”来启动它,然后执行你想要捕获的任何内容,然后点击control-D来closures脚本文件。 我不知道sh / bash / ksh的等价物。

另外,由于您现在已经指出这些是您可以修改的sh脚本,因此您可以通过用括号或括号括起整个脚本来进行内部redirect,例如

  #!/bin/sh { ... whatever you had in your script before } 2>&1 | tee output.file 

接近半年后…

我相信这是OP所要求的“完美解决scheme”。

以下是您可以添加到Bash脚本顶部的一行代码:

 exec > >(tee -a $HOME/logfile) 2>&1 

下面是一个小脚本,演示它的用法:

 #!/usr/bin/env bash exec > >(tee -a $HOME/logfile) 2>&1 # Test redirection of STDOUT echo test_stdout # Test redirection of STDERR ls test_stderr___this_file_does_not_exist 

(注意:这只适用于Bash, 不适用于/ bin / sh。)

改编自这里 ; 原来没有,从我可以告诉,捕获日志文件中的STDERR。 固定从这里注意。

我创build了一个名为“RunScript.sh”的脚本。 这个脚本的内容是:

 ${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log 

我这样称呼它:

 ./RunScript.sh ScriptToRun Param1 Param2 Param3 ... 

这是有效的,但它需要通过外部脚本运行应用程序的脚本。 这有点笨拙。

将stderrredirect到stdout将其添加到您的命令中: 2>&1对于输出到terminal并login文件,您应该使用tee

两者在一起看起来是这样的:

  mycommand 2>&1 | tee mylogfile.log 

编辑:embedded到您的脚本,你会做同样的。 所以你的脚本

 #!/bin/sh whatever1 whatever2 ... whatever3 

最终会成为

 #!/bin/sh ( whatever1 whatever2 ... whatever3 ) 2>&1 | tee mylogfile.log 

使用tee程序和dup stderr标准输出。

  program 2>&1 | tee > logfile 

一年后,这里有一个旧的bash脚本来logging任何东西。 例如,
teelog make ...将日志logging到生成的日志名称(并查看用于logging嵌套make的技巧)。

 #!/bin/bash me=teelog Version="2008-10-9 oct denis-bz" Help() { cat <<! $me anycommand args ... logs the output of "anycommand ..." as well as displaying it on the screen, by running anycommand args ... 2>&1 | tee `day`-command-args.log That is, stdout and stderr go to both the screen, and to a log file. (The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out; see http://en.wikipedia.org/wiki/Tee_(command) ). The default log file name is made up from "command" and all the "args": $me cmd -opt dir/file logs to `day`-cmd--opt-file.log . To log to xx.log instead, either export log=xx.log or $me log=xx.log cmd ... If "logdir" is set, logs are put in that directory, which must exist. An old xx.log is moved to /tmp/\$USER-xx.log . The log file has a header like # from: command args ... # run: date pwd etc. to show what was run; see "From" in this file. Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file: command args ... > `day`-command-args.log and tees stderr to both the log file and the terminal -- bash only. Some commands that prompt for input from the console, such as a password, don't prompt if they "| tee"; you can only type ahead, carefully. To log all "make" s, including nested ones like cd dir1; \$(MAKE) cd dir2; \$(MAKE) ... export MAKE="$me make" ! # See also: output logging in screen(1). exit 1 } #------------------------------------------------------------------------------- # bzutil.sh denisbz may2008 -- day() { # 30mar, 3mar /bin/date +%e%h | tr '[AZ]' '[az]' | tr -d ' ' } edate() { # 19 May 2008 15:56 echo `/bin/date "+%e %h %Y %H:%M"` } From() { # header # from: $* # run: date pwd ... case `uname` in Darwin ) mac=" mac `sw_vers -productVersion`" esac cut -c -200 <<! ${comment-#} from: $@ ${comment-#} run: `edate` in $PWD `uname -n` $mac `arch` ! # mac $PWD is pwd -L not -P real } # log name: day-args*.log, change this if you like -- logfilename() { log=`day` [[ $1 == "sudo" ]] && shift for arg do log="$log-${arg##*/}" # basename (( ${#log} >= 100 )) && break # max len 100 done # no blanks etc in logfilename please, tr them to "-" echo $logdir/` echo "$log".log | tr -C '.:+=[:alnum:]_\n' - ` } #------------------------------------------------------------------------------- case "$1" in -v* | --v* ) echo "$0 version: $Version" exit 1 ;; "" | -* ) Help esac # scan log= etc -- while [[ $1 == [a-zA-Z_]*=* ]]; do export "$1" shift done : ${logdir=.} [[ -w $logdir ]] || { echo >&2 "error: $me: can't write in logdir $logdir" exit 1 } : ${log=` logfilename "$@" `} [[ -f $log ]] && /bin/mv "$log" "/tmp/$USER-${log##*/}" case ${0##*/} in # basename log | Log ) # both to log, stderr to caller's stderr too -- { From "$@" "$@" } > $log 2> >(tee /dev/stderr) # bash only # see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe ;; * ) #------------------------------------------------------------------------------- { From "$@" # header: from ... date pwd etc. "$@" 2>&1 # run the cmd with stderr and stdout both to the log } | tee $log # mac tee buffers stdout ? esac 

script中使用script命令(man 1脚本)

创build一个包装脚本(2行),设置脚本(),然后调用退出。

第1部分:wrap.sh

 #!/bin/sh script -c './realscript.sh' exit 

第2部分:realscript.sh

 #!/bin/sh echo 'Output' 

结果:

 ~: sh wrap.sh Script started, file is typescript Output Script done, file is typescript ~: cat typescript Script started on fr. 12. des. 2008 kl. 18.07 +0100 Output Script done on fr. 12. des. 2008 kl. 18.07 +0100 ~: