从Shell脚本内部将所有标准输出及标准错误显示在屏幕并同时写入文件的方法

产生这个话题的缘由:

在编写稍微复杂的Shell脚本时,我们常常需要将标准输出和标准错误信息记录下来,以往我们通过如下形式办到:
somescript.sh > log 2>&1
但这对规范的shell是不太完美的,一是log文件的位置及名称,只能由着执行者来定,存在不确定性;二是执行者是否记得使用这样的句式来确保操作显示有记录,也无法保证

所以,我们需要在shell脚本内部指定,不受执行者影响而记录下显示输出的手段,而且,我们还不能用愚蠢的每句后面来一个| tee $logfile的方式

以下为实现方法,以Korn Shell为准:

变量:
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,比较方便的方法是print "\015"(015是8进制,换算成10进制就是13,即Ctrl-D的ASCII码)

最后写法:
test.sh

  1. logfile=test.log
  2. fifofile=test.fifo

  3. mkfifo $fifofile
  4. cat $fifofile | tee $logfile &

  5. exec 1>$fifofile
  6. exec 2>&1

  7. # some commands to produce normal and error output
  8. cal
  9. badcommand to generate stderr messages
  10. #

  11. print "\015"
复制代码


运行结果:
  1. [root@system:/tt] sh test.sh
  2.       October 2008
  3. Sun Mon Tue Wed Thu Fri Sat
  4.             1   2   3   4
  5. 5   6   7   8   9  10  11
  6. 12  13  14  15  16  17  18
  7. 19  20  21  22  23  24  25
  8. 26  27  28  29  30  31

  9. test.sh[12]: badcommand:  not found.
  10. [root@system:/tt] cat test.log
  11.       October 2008
  12. Sun Mon Tue Wed Thu Fri Sat
  13.             1   2   3   4
  14. 5   6   7   8   9  10  11
  15. 12  13  14  15  16  17  18
  16. 19  20  21  22  23  24  25
  17. 26  27  28  29  30  31

  18. test.sh[12]: badcommand:  not found.
  19. [root@system:/tt]
复制代码

[ 本帖最后由 larryh 于 2008-10-8 15:03 编辑 ]

作者: larryh   发布时间: 2008-10-08

somescript.sh 2>&1 | tee $logfile,以前都用这个,现在又学一招

作者: jnpiero   发布时间: 2008-10-08

学习了。谢谢老大分享

作者: 五“宅”一生   发布时间: 2008-10-08

测试后有个地方不太明白
因为cat管道文件内容的时候,永远不会结束,因为不会遇到EOF标记(就是控制字符Ctrl-D),除非在管道中出现了Ctrl-D,所以我们在脚本最后需要显示一个Ctrl-D
在这个例子中 是否执行print "\015" 有什么不同的结果?没测出来

作者: meteor06   发布时间: 2008-10-09

原帖由 meteor06 于 2008-10-9 20:15 发表
在这个例子中 是否执行print "\015" 有什么不同的结果?没测出来


cat是放在后台的,如果不执行这一条,完成以后,cat不会退出,你ps -ef会看到cat进程还在,多执行几次就会有很多cat进程不能结束了

作者: larryh   发布时间: 2008-10-16

这个试了下,脚本执行完成后,cat 也就没了,没在后台运行
以前一直用mknod .. p 建立管道,看了下和 mkfifo 的应该一样
exec 1>$fifofile
exec 2>&1
在脚本内部将输出,错误写到文件还是很实用的

能将屏幕信息都转到文件里的,只试过 script ,主要可以将输入也记录了
但是文件记录信息会滞后,还没找到有什么参数可设置

作者: meteor06   发布时间: 2008-10-18

很好 学习了 谢谢

作者: dfrog   发布时间: 2011-08-17