构建工具生成一个守护进程,但该进程不会关闭其标准输出。如何防止这种情况导致我的 shell 管道停滞?

构建工具生成一个守护进程,但该进程不会关闭其标准输出。如何防止这种情况导致我的 shell 管道停滞?

我正在使用(闭源)特定于供应商的构建工具(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也会发生同样的事情),脚本就不会停顿。cattee
  • 如果我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根本检测不到它。

相关内容