我试图调整使用以下内容的脚本:
# first portion of script
# ...
exec > >(tee -ia $OUT)
# ...
# second portion of script
我对这个脚本的问题是它会向stdout
(我的终端)产生大量输出。脚本作者没有包含消除终端输出的选项。我想添加一个选项删除stdout
, 并仅在文件 中获取输出$OUT
。
这是我尝试过的:
TERM_OPT="OFF"
# first portion of script
# ...
if [ $TERM_OPT != "OFF" ]; then
exec > >(tee -ia $OUT)
else {
# ...
# second portion of script
} > $OUT
fi
这似乎有效,但我不确定其用途大括号 {}
在此背景下作为分组命令的 GNU 部分(好像) 说明;
列表后面需要有分号。但添加;
或删除似乎没有什么区别。我想知道是否应该使用括号()
而不是花括号,但这会导致 中的所有内容都()
在子 shell 中执行。我对此不是特别热衷,因为这是别人的剧本&子外壳我不清楚其含义(我没有尝试过)。
我尝试过的另一件事看起来像一个黑客,但我读到其他人使用它,它似乎也工作正常:
TERM_OPT="OFF"
# first portion of script
# ...
if [ $TERM_OPT != "OFF" ]; then
exec > >(tee -ia $OUT)
else
exec > >(tee -ia $OUT 1> /dev/null)
fi
# ...
# second portion of script
我喜欢这个,因为它看起来更独立的,但这并不是一个值得考虑的事情。
所以问题是: 这样做的正确方法是什么?我的意思是,正确的方法是什么选择退出exec > >(tee -ia $OUT)
?后的终端输出这些解决方案中的一种比另一种更可取——还是我需要做一些完全不同的事情?
答案1
仅有的$OUT
在挂起模式下重定向到文件a
要容易得多:
case $TERM_OPT in
(OFF) exec >> "$OUT";;
(*) exec > >(tee -ia -- "$OUT")
esac
请注意,shell 不会等待tee
退出,因此您可能会发现tee
脚本完成后仍在输出某些内容。
$ bash -c 'exec > >(tee -ia -- file); echo A'; echo B; sleep 1
B
A
看看A
B之后的输出如何。
要解决这个问题,您可以添加:
if [ "$TERM_OPT" != OFF ]; then
exec > /dev/null # closes the pipe to tee which should cause tee
# to see EOF on input and exit
wait # waits for all asynchronous tasks including tee
fi
在脚本的末尾或EXIT
陷阱中。
另一个选择(也可以在 sh 中使用)是将整个脚本放入一个main
函数中并执行以下操作:
#! /bin/sh -
main() {
# your script here
}
case $TERM_OPT in
(OFF) main "$@" >> "$OUT";;
(*) main "$@" | tee -ia -- "$OUT"
esac
答案2
如果您想重定向 stdout 以附加到文件,并且不在终端上保留输出的副本,只需执行以下操作:
exec >> "$OUT"
的唯一目的tee
是将一个流复制为多个流,因此如果您只想将输出放在一个位置,则不需要它。你tee -ia "$OUT" > /dev/null
当然也可以工作,但这有点愚蠢:你仍然会复制输出,只是为了丢弃另一个副本。
{}
我不确定在这种情况下使用花括号
大括号需要分号或换行符。其实手册上是这么说的:“列表后面需要分号(或换行符)。”括号两者都不需要。
子 shell 的含义是,子 shell 在 shell 的独立副本中运行,子 shell 中的任何更改在其外部都是不可见的。如果脚本在子 shell 结束时结束,那没有多大意义,子 shell 所做的只是启动一个不需要的进程。
我会选择类似的东西
if [ "$TERM_OPT" != "OFF" ]; then
exec > >(tee -ia -- "$OUT")
else
exec >> "$OUT"
fi