强制bash脚本使用tee而不使用命令行管道


20

我有一个带有大量输出的bash脚本,我想更改该脚本(而不是创建一个新脚本)以将所有输出复制到一个文件中,就像通过tee通过管道将其复制一样。

我有一个文件script.sh

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

而且我想将STDOUT复制到文件script.log,而不必./script.sh | tee script.log每次都键入。

谢谢汤姆


抱歉,我没有这么早说,但是脚本很长,这就是为什么我要寻找一个简单的解决方案。追加中| 每条线的tee太多了。
汤姆(

Answers:


15

我无法让Dennis的单线工作很简单,所以这是一个复杂得多的方法。我会先尝试一下。

如前所述,您可以使用exec重定向整个脚本的标准错误和标准输出。像这样:
exec > $LOGFILE 2>&1 这会将所有stderr和stdout输出到$ LOGFILE。

现在,由于您希望将此内容以及日志文件显示在控制台中,因此您还必须使用命名管道来使exec写入和发球以便读取。
(尽管显然丹尼尔的单线方式也不同,但从技术上讲,丹尼尔的单线也可以这样做)mkfifo $PIPEFILE。然后执行以下操作。

#开始将tee写入日志文件,但从命名管道中提取其输入。
三通$ LOGFILE <$ PIPEFILE&

#捕获等待命令的T恤的进程ID。
TEEPID = $!

#将其余的stderr和stdout重定向到我们的命名管道。
exec> $ PIPEFILE 2>&1

回显“在此处进行命令”
回声“他们所有的标准将被淘汰。”
回显“他们的标准错误也是如此”>&2

#关闭stderr和stdout文件描述符。
exec 1>&-2>&-

#等待三通完成,因为管道的另一端已经关闭。
等待$ TEEPID

如果想彻底了解,可以在脚本的开头和结尾创建和销毁命名管道文件。

作为记录,我从随机人的非常有信息的博客文章中收集了其中的大部分内容:(存档版本


这似乎在cygwin中不起作用。:-(
docwhat 2010年

这篇文章很好地进行了教育。非常感谢你。
费利佩·阿尔瓦雷斯

27

只需将其添加到脚本的开头即可:

exec > >(tee -ia script.log)

这样会将发送到stdout的所有输出附加到文件中script.log,保留先前的内容。如果要在每次运行脚本时重新启动,只需rm script.log在该exec命令前添加-atee命令中删除。

-i选项导致tee忽略中断信号,并可能允许tee捕获更完整的输出集。

添加此行还可以捕获所有错误(在单独的文件中):

exec 2> >(tee -ia scripterr.out)

多个>符号之间的空格很重要。


非常有趣的解决方案。
ℝaphink

2
与我发布的所有废话相比,这是一种非常优雅的方法。但是,当我创建一个包含这两行的脚本时,它会挂起,并且永远不会正确退出。
Christopher Karel

什么版本的Bash?
暂停,直到另行通知。

GNU bash,版本3.2.25(1)作为RHEL5.3安装的一部分。看来这是官方存储库中的最新版本。
Christopher Karel

我只是在Bash 3.2.49(23)-发行版中的cygwin下尝试过,但是得到了文件描述符错误。我在Ubuntu 9.10的Bash 4.0.33(1)-发行版中工作,因此它可能是Bash 4的事情。
暂停,直到另行通知。

6

这是Dennis Williamson先前发布的答案的组合版本。以正确的顺序将err和std都追加到script.log。

将此行添加到脚本的开头:

exec > >(tee -a script.log) 2>&1

注意>标志之间的空间。在GNU bash 3.2.25(1)-发行版(x86_64-redhat-linux-gnu)上对我有效


5

我想另一种方法是这样的:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 

3

如果要输出的副本,可以使用tee以下方式:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

但是,这只会将标准输出记录到script.log。如果要确保将stderr和stdout都重定向,请使用:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

您甚至可以通过一些小功能使其变得更好:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini

好答案。如果他真的只在乎stdout,我就把2>&1它留在外面,然后用管道把它塞到开球处。
DaveParillo

是的,我只包含stderr,因为Igor先前的回答很想重定向它,我认为这是个好主意。
ℝaphink

1

您将使用此:

script logfile.txt
your script or command here

这会将所有内容输出到该日志,所有内容,并将其显示在屏幕上。如果您想要FULL输出,这就是方法。

然后,如果您很懒惰,则可以重播脚本。


最好注意这script是一个包装,例如。sudo apt-get install script将其安装在Debian 风格的发行版上,还script -ac 'command opts' ~/Documents/some_log.script可以通过类似以下方式在终端中进行审查strings ~/Documents/some_log.script | morestrings是预先消毒某些script 注入的简单方法,以允许使用更高级的重放 /查看方法。否则,@ Phishy是一个可靠的答案,因为它script也保留了交互式内容。
S0AndS0

0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

您可以在此处查看有关其工作原理的更多信息:http : //www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/


1
我在Igor的答案的第二行中添加了第二个>,以免擦除第一行中写入的日志。
道格·哈里斯

1
尽管他想要一份副本,所以他需要使用tee
phaphink

@Raphink:是的:)
Tom
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.