解释bash命令"exec > >(tee $LOG_FILE) 2>&1"

12

我的意图是将bash脚本的所有输出显示在控制台并记录到文件中。

这是我的预期工作的脚本。

#!/bin/bash

LOG_FILE="test_log.log"
touch $LOG_FILE

# output to console and to logfile
exec > >(tee $LOG_FILE) 2>&1

echo "Starting command ls"
ls -al
echo "End of script"

然而我不明白为什么它会那样工作。

我预期exec >>(tee $LOG_FILE) 2>&1能够工作,但它失败了,尽管exec >$LOG_FILE 2>&1确实可以工作。

我在bash手册高级bash脚本编程指南中都找不到关于exec > >(command )结构的原因。你能解释一下其背后的逻辑吗?

1个回答

20
>(tee $LOG_FILE)进程替换的一个例子,您可能希望搜索相关内容。高级Shell脚本编程Bash手册
使用语法<(program)来捕获输出和>(program)来提供输入,我们可以一次传递一个记录的数据。它比命令替换(反引号或$( ))更强大,因为它替换的是一个文件名,而不是文本。因此,在任何需要指定文件的地方,我们都可以替换程序的标准输出或输入(虽然在输入上进行进程替换并不常见)。这特别有用,当一个程序不使用标准流来实现你想要的时候。
请注意,在您的示例中,您缺少一个空格,exec >>(tee $LOG_FILE) 2>&1错误的(会导致语法错误)。相反,
exec > >(tee $LOG_FILE) 2>&1

正确的做法是,空格很关键。

所以,exec > 部分会改变文件描述符1(默认值),也称为 stdout 或标准输出,使其指向“接下来的任何内容”,在这种情况下,它是进程替换,尽管通常它是一个文件名。

2>&1 将文件描述符2 (stderr 或标准错误) 重定向到与文件描述符1 (stdout 或标准输出) 相同的位置。重要提示:如果您省略了 &,则会得到一个名为 1 的文件,而不是成功的重定向。

一旦您调用上面的 exec 行,则已更改当前进程的标准输出,因此随后的命令输出将转到该 tee 进程而不是常规的 stdout


我在多个帖子中看到了这个命令,但无论我尝试什么,它都不起作用。我不知道我缺少了什么。我一直收到错误信息:syntax error near unexpected token `>' 有人知道我缺少了什么吗? - Kenny
啊,我找到了我的错误所在。我在调用包含命令的脚本时使用了"sh <path-to-script>"。当我使用"bash <path-to-script>"时它可以工作。该脚本以“#/bin/bash”开头,我以为这将使用Bash运行脚本,但我猜我对Unix中的Shell脚本还有很多需要学习:) 。 - Kenny
@Kenny 如果你在命令行上指定了shell,那么#!前缀(我假设缺少的!是一个打字错误)将被视为注释。 shbash不同。 在某些平台上,/bin/sh是指向/bin/bash的符号链接,但是如果以这种方式运行它,bash会聪明地尝试表现得像sh(程序可以找出调用它的名称)。 - cdarke
@cdarke,确实是个打字错误 :) 感谢您的澄清,非常有帮助! - Kenny
使用 exec > >(tee $LOG_FILE) 2>&1 对于记录作为 cron 作业调用的可执行脚本的所有输出是非常有用的!请参见此问答:Super User: How can I view results of my cron jobs?,特别是这个答案 - Gabriel Staples

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接