我正在使用(闭源)特定于供应商的构建工具(Microsemi Designer,一种 FPGA 布局工具)。我从 shell 脚本(高度简化)调用它:
... # Setup
/opt/.../designer SCRIPT:my_script.tcl |tee build.log
... # Postprocessing
问题是:即使designer
进程终止后,该tee
进程仍继续运行,因此脚本无法继续运行。
据我所知,这是因为designer
生成了一个长寿命守护进程,
windu_scmd50
并且该守护进程不会关闭其标准输出(它从 继承designer
)。因此,它tee
正在等待其标准输入(连接到管道)上的 EOF,但永远不会出现。
有证据表明这是问题的原因:
ps
显示 tee 进程仍在运行,但设计器进程没有运行。- 当我终止该
windu_scmd50
进程时,tee
它立即终止并且脚本继续(但我无法在生产中这样做)。 - 如果我没有将 的输出通过管道传输
designer
到(或传输到其他任何东西;用 代替 时tee
也会发生同样的事情),脚本就不会停顿。cat
tee
- 如果我
designer
从 Jenkins 运行(没有将输出传输到文件),Jenkins 会通知我“进程泄露了文件描述符”,链接到 此帮助页面(显然,詹金斯明确处理了这种情况)。
所以:我如何确保tee
进程在终止时(或终止后不久)终止designer
?
我考虑过的一些方法:
- 是否可以重定向正在运行的进程的标准输出?
- 是否有可以代替 shell 管道的工具可以检测进程的终止
designer
?
一些不可能的方法:
- 我不能忽略该
designer
过程的输出;我需要将其写入日志文件。 - 我无法终止该
windu_scmd50
进程,因为它可能被机器上的其他进程使用。 - 上描述的解决方法Jenkins 帮助页面似乎不适用,因为它们也会使 的输出静音
designer
。 - 我无法修改工具安装以
windu_scmd50
用包装器替换可执行文件。
更多说明:
- 记录
windu_scmd50
流程的输出会很好,但这不是必需的。不过,我确实需要记录设计器流程的输出。 - 实际上,
designer
和之间还有另一个过滤器tee
,用于添加时间戳(designer ... |ts -s |tee build.log
) - 可以使用
bash
特定于的功能;兼容性sh
不是必需的。
下面是一个演示该问题的最小示例(test.sh
):
#!/bin/bash
echo "Start"
sleep 10 & # Background process that does not close stdout
echo "End"
直接调用此脚本 ( ./test.sh
) 会打印“Start”和“End”并立即完成。将输出传输到另一个进程 ( ./test.sh |cat
) 会立即打印“Start”和“End”,但随后会暂停 10 秒钟才完成。
答案1
一个相当简单的解决方法是在进程终止后向流中注入一条消息designer
。当遇到该消息时,cat
类似过滤器前置应该退出。tee
它将是这样的:
{ /opt/.../designer SCRIPT:my_script.tcl; echo message; } \
| sed -n '/^message$/q;p' | tee build.log
笔记:
如果
designer
(或任何继承其 stdout 的东西)打印了该消息,sed
则会提前退出。选择一个sTr1ng_Unlik3ly t0-Be enCOUnTered_in vvhat designer PRinT5
。ASCII EOT 字符(八进制 004)可能是一个不错的选择:
{ …; printf '\004\n'; } | sed -n "/^$(printf '\004')\$/q;p" | …
处理能力是有限制的
sed
。参见这个答案关于grep
,与 类似sed
。^message$
匹配仅包含以下内容的完整行message
。这假设进程的输出(如果有)designer
以换行符结尾(即最后一行是完整的;请参阅线对比不完整的线);只有这样message
才会在自己的行中。如果designer
肯定会生成一条不完整的行,那么您需要sed -n '/message$/{s/message$//;p;q};p'
。如果两种方式都可以,那么您需要sed -n '/^message$/q;/message$/{s/message$//;p;q};p'
。请注意,来自的不完整designer
行将变成一条完整的行。非沉默的后代
designer
可能会干扰:- 它们可能会导致
message
出现中间行(即使它们试图输出完整的行)。对于我们来说,sed
这就像designer
生成了一条不完整的行。 - 如果你选择的消息很长,那么它可能会与其他数据交错出现在管道中(见这个答案和
PIPE_BUF
这里)。在这种情况下我们sed
根本检测不到它。
- 它们可能会导致