使用tee命令时,重定向的输出会卡住怎么办?

8

我想在bash脚本中提供一个可选的日志参数,并使用exec从一开始就tee一个管道。但是,打开tee进程会导致脚本挂起,我认为是因为stdout没有关闭:

# Output to a log file, if set
if [[ $OPT_LOG ]]; then
    exec > >(tee -a $OPT_LOG)
fi

我试图关闭:

exec >&-

但它仍然挂起 - 是否还有其他正确关闭tee的方法,以便脚本在执行结束时正常退出?


1
这段代码(自由散布分号以便可以从此评论中复制和粘贴)在我的Mac OS X 10.10.3上正常退出。 “ OPT_LOG = file.name; 如果[[ $OPT_LOG ]]; 然后执行> >(tee-a $ OPT_LOG); fi; for ((i = 0; i <10; i ++)); do echo“在$(date)记录消息$ i”; 睡眠1; done (我原来的唯一分号是then`前面的那个,而且只是因为我从问题中复制了代码 - 这不是我编写shell脚本的方式)。你的问题可能在你没有展示的代码中。你运行什么命令? 从bash -x yourscript.sh得到什么? - Jonathan Leffler
1
@Jonathan Leffler: 如果从你的例子中移除 sleepdate 调用,根据 Andrew 的说法,脚本将不再正常退出: OPT_LOG=file.name; if [[ $OPT_LOG ]]; then exec > >(tee -a $OPT_LOG); fi; for ((i = 0; i < 10; i++)); do echo "Logging message $i"; done - Eugeniu Rosca
@chatraed:这是一个有趣的观察!我可以重现你的结果。我做了一些实验。我在“for”循环之前添加了一个“pwd”,只是回显,但这并没有影响到事情(但“pwd”可能是内置的)。我添加了“bash -c 'exit 0'”(这不是内置的),代码正常终止。我尝试使用“> >(tee -a $OPT_LOG &)”,输出没有出现(我相信它应该出现,但Bash似乎希望它表现得不同)。我的印象是Andrew发现了Bash中可能会报告的错误;我认为不退出不是可以接受的。 - Jonathan Leffler
@chatraed:它也证实了你的解决方法是可行的,尽管比最低要求慢。对于生产使用,我会选择比睡眠更快的东西 - 除了sleep 0可能很好和快速之外。另外,一个 shell 脚本不调用任何外部命令相对不寻常,在 exec 重定向后调用任何外部命令似乎可以抑制这个 bug。 - Jonathan Leffler
哦,我在原始脚本中过度宣称了分号的缺失;当然,“for ((i = 0; i < 10; i++))”这一行需要两个分号。 - Jonathan Leffler
2个回答

6

似乎由于shell脚本未退出,使用tee会导致提示符($PS1)不再出现。作为解决方法,我通常在调用tee后使用短暂的sleep

#!/bin/bash

exec > >(tee -a mylog)
sleep .1
# my code

我刚花了3个小时试图弄清楚为什么我的 shell 脚本会卡住。你救了我的晚上。谢谢! - vicTROLLA

6

将注释转换为答案,并进行小的编辑。

注意到

The following code exits OK for me on Mac OS X 10.10.3.

OPT_LOG=file.name
if [[ $OPT_LOG ]]
then exec > >(tee -a $OPT_LOG)
fi
for ((i = 0; i < 10; i++))
do
    echo "Logging message $i at $(date)"
    sleep 1
done

Your problem is probably in the code you've not shown. What commands are you running? What do you get from bash -x yourscript.sh?

chatraed观察到

If you remove the sleep and date calls from your example, the script does not exit properly any more, as told by Andrew:

OPT_LOG=file.name;
if [[ $OPT_LOG ]]; then exec > >(tee -a $OPT_LOG); fi;
for ((i = 0; i < 10; i++)); do echo "Logging message $i"; done

我回答道:

这是一个有趣的观察!我可以重现你的结果。

我做了一些实验:

  • 我在for循环之前添加了pwd,只是输出,但这并没有影响到结果(但pwd可能是Bash内置的命令)。
  • 我添加了bash -c 'exit 0'(这不是内置命令),代码正常终止。
  • 我尝试使用> >(tee -a $OPT_LOG &),但输出没有出现在屏幕或文件中。(我觉得这很奇怪,但这只是一种尝试性的解决方法,不是主要工作的一部分。)

我的印象是Andrew发现了Bash的一个bug,可以进行报告。(请参阅Bash手册上的Reporting Bugs了解如何报告。)我认为,如果没有执行任何外部命令,那么不退出是不正确的。我可以确认苹果的Bash 3.2.57有这个问题;Bash 4.3.27也有这个问题(我自己构建的,因此修补了ShellShock漏洞的修复程序)。

相对不常见的是,shell脚本没有调用任何外部命令,在exec重定向之后调用任何外部命令似乎都会抑制这个bug。

它还证实了chatraed解决方法有效,尽管比最小必要的更慢。对于生产使用,我会选择sleep 0,它比sleep 0.1快近十分之一秒。只睡眠几分之一秒仅适用于某些系统;经典地(并根据POSIX),sleep不会睡眠小数秒。另一方面,如果你写sleep 0.1,在不支持小数秒的系统上,包括前导的0可能使它睡眠零秒;写.1可能有相同的效果,也可能没有。


好的发现和总结! - Eugeniu Rosca
下一步可能是使用help提取shell内置命令列表,并检查它们的行为是否一致。 - Eugeniu Rosca
这个问题是否有Bash的错误报告? - Steve Amerige
@SteveAmerige — 我完全不记得提交过一个。我还没有检查它是否在2021年仍然是个问题。 - Jonathan Leffler

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