247

是否可以将 Bourne shell 脚本的所有输出重定向到某个地方,但在脚本本身内部使用 shell 命令?

重定向单个命令的输出很容易,但我想要更像这样的东西:

#!/bin/sh
if [ ! -t 0 ]; then
    # redirect all of my output to a file here
fi

# rest of script...

含义:如果脚本以非交互方式运行(例如,cron),则将所有内容的输出保存到文件中。如果从 shell 以交互方式运行,让输出像往常一样转到标准输出。

我想为通常由 FreeBSD 定期实用程序运行的脚本执行此操作。这是日常运行的一部分,我通常不关心每天在电子邮件中看到它,所以我没有发送它。但是,如果这个特定脚本中的某些东西失败了,那对我来说很重要,我希望能够捕获并通过电子邮件发送日常工作的这一部分的输出。

更新:Joshua 的回答很准确,但我也想围绕整个脚本保存和恢复 stdout 和 stderr,这样做是这样的:

# save stdout and stderr to file 
# descriptors 3 and 4, 
# then redirect them to "foo"
exec 3>&1 4>&2 >foo 2>&1

# ...

# restore stdout and stderr
exec 1>&3 2>&4
4

5 回答 5

226

解决更新后的问题。

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

大括号 '{ ... }' 提供了一个 I/O 重定向单元。大括号必须出现在命令可能出现的地方——简单地说,在行首或分号之后。(是的,这可以更精确;如果您想狡辩,请告诉我。

您是对的,您可以使用您显示的重定向保留原始 stdout 和 stderr,但是对于以后必须维护脚本的人来说,如果您将重定向代码的范围设置为如上所示,则通常更容易理解发生了什么。

Bash 手册的相关部分是Grouping CommandsI/O Redirection。POSIX shell 规范的相关部分是Compound CommandsI/O Redirection。Bash 有一些额外的符号,但在其他方面类似于 POSIX shell 规范。

于 2008-11-24T19:11:28.293 回答
214

通常,我们会将其中之一放置在脚本顶部或附近。解析其命令行的脚本将在解析后进行重定向。

将标准输出发送到文件

exec > file

与标准错误

exec > file                                                                      
exec 2>&1

将 stdout 和 stderr 都附加到文件

exec >> file
exec 2>&1

正如Jonathan Leffler 在他的评论中提到的

exec有两个独立的工作。第一个是用新程序替换当前正在执行的shell(脚本)。另一个是更改当前 shell 中的 I/O 重定向。这与没有参数不同exec

于 2008-11-24T16:29:52.957 回答
40

您可以使整个脚本成为这样的函数:

main_function() {
  do_things_here
}

然后在脚本的末尾有这个:

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

或者,您可以在每次运行时将所有内容记录到日志文件中,并且仍然通过简单地执行以下操作将其输出到标准输出:

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log
于 2008-11-24T16:44:54.783 回答
9

要保存原始标准输出和标准错误,您可以使用:

exec [fd number]<&1 
exec [fd number]<&2

例如,以下代码将“walla1”和“walla2”打印到日志文件(a.txt),“walla3”打印到stdout,“walla4”打印到stderr。

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6
于 2015-04-15T20:46:25.300 回答
4
[ -t <&0 ] || exec >> test.log
于 2012-06-27T12:47:37.757 回答