如何从脚本内部将 stdout 和 stderr 重定向到文件以及终端。
#!/bin/bash
LOG_FILE="/tmp/abc.log"
exec &> >(tee -a $LOG_FILE)
echo "Start"
echo "Hi" 1>&2
echo "End"
我找到了上面的脚本。但这个脚本只适用于 bash。使用 sh shell 会出现以下错误:
./abc.sh: 5: Syntax error: redirection unexpected (expecting word)
我想要一个可以与 sh 和 bash shell 一起使用的脚本。
答案1
是的,&>
是一个bash
运算符(现在也被 所支持zsh
,而zsh
始终具有>&
与 in 相同的功能csh
),以及>(...)
一个ksh
运算符(现在也被zsh
和支持bash
),两者都不是sh
运算符。那个不带引号的 $LOG_FILE ,你显然不想在这里 split+glob ,使它成为 zsh 语法(唯一一个 shell 中 split+glob 不会在不带引号的扩展上隐式执行,尽管在 中zsh
,你只需这样做exec >&1 > $LOG_FILE 2>&1
)。
在sh
语法上,你可以这样做:
#! /bin/sh -
LOG_FILE="/tmp/abc.log"
{
# your script here
} 2>&1 | tee -- "$LOG_FILE"
或者将所有内容放入一个函数中:
#! /bin/sh -
LOG_FILE="/tmp/abc.log"
main() {
# your script here
}
main "$@" 2>&1 | tee -- "$LOG_FILE"
无论如何,这种方法和您的zsh
-style 方法最终都会在与标准输出上打开的相同资源上打印错误消息。因此,如果有人这样做your-script > out 2> err
,err
将会是空的并且out
将包含正常输出和错误。
为了确保保留输出和错误的原始目的地,您可以这样做:
{
{
main "$@" 3>&- | tee -a -- "$LOG_FILE" >&3 3>&-
} 2>&1 | tee -a -- "$LOG_FILE" >&2 3>&-
} 3> "$LOG_FILE" 3>&1
(未经测试)。
另请注意,从安全角度来看,在全局可写目录中使用具有固定名称的文件是/tmp
不好的做法,因为其他一些用户可能会放置具有相同名称的恶意符号链接,这会导致您覆盖它指向的文件。