tee - 复制输出到控制台和文件
Shell内部将输出显示在屏幕并同时写入文件
产生这个话题的缘由:
在编写稍微复杂的Shell脚本时,我们常常需要将标准输出和标准错误信息记录下来,以往我们通过如下形式办到:
somescript.sh > log 2>&1
但这对规范的shell是不太完美的,一是log文件的位置及名称,只能由着执行者来定,存在不确定性;二是执行者是否记得使用这样的句式来确保操作显示有记录,也无法保证。
所以,我们需要在shell脚本内部指定,不受执行者影响而记录下显示输出的手段,而且,我们还不能用愚蠢的每句后面来一个| tee $logfile
的方式。
以下为实现方法,以Korn Shell为准(测试下来,bash也一样可以用):
变量: logfile – 所有信息输出的文件 fifofile – 为同时输出到屏幕和文件所建立的管道文件
Shell内部可以支持的重定向标准输出和标准错误设备的基本方法:
exec 1>$logfile exec 2>&1
但是,这样就只能将所有信息输出到\(logfile
,无法实现同时显示在屏幕的目的。不可能有exec 1>|tee \)logfile
的用法
以往,在命令行将错误输出也导向屏幕及文件的方法是:
somescript.sh 2>&1 | tee $logfile
这里用到管道,而exec命令并不支持管道用法,所以我们需要建立fifo文件来完成
但是,fifo管道文件是阻塞形管道,没有随时将其内容输出的话,脚本将hang住无法继续,所以我们要用cat 管道文件
的方式将其随时导出,为了不影响后续命令执行,cat这一句必须放到后台。因为cat管道文件内容的时候,永远不会结束,因为不会遇到EOF标记(就是控制字符Ctrl-D),除非在管道中出现了Ctrl-D,所以我们在脚本最后需要显示一个Ctrl-D,比较方便的方法是printf "\015"
(015是8进制,换算成10进制就是13,即Ctrl-D的ASCII码)
最后写法:
-
test.sh
#!/bin/sh logfile=test.log fifofile=test.fifo rm -f "$fifofile" mkfifo $fifofile cat $fifofile | tee -a $logfile & exec 3>&1 exec 4>&2 exec 1>$fifofile exec 2>&1 echo "some commands to produce normal and error output" badcommand to generate stderr messages printf "\015" exec 1>&3 exec 2>&4 rm -f "$fifofile" echo "done."
-
运行结果:
./test.sh;echo "logfile:";cat test.log some commands to produce normal and error output ./test.sh: line 13: badcommand: command not found done. logfile: some commands to produce normal and error output ./test.sh: line 13: badcommand: command not found
参考文献larryh. 从Shell脚本内部将所有标准输出及标准错误显示在屏幕并同时写入文件的方法. [EB/OL], 2008-10-8. [2011-05-23]. http://www.loveunix.net/thread-90320-1-1.html