管道到头部导致从 python 调用的 shell 脚本中的管道损坏

Posted

技术标签:

【中文标题】管道到头部导致从 python 调用的 shell 脚本中的管道损坏【英文标题】:Piping to head results in broken pipe in shell script called from python 【发布时间】:2016-01-06 08:51:19 【问题描述】:

我要运行一个命令来生成随机字符串:

var=`< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c8`

当我在交互式 bash 会话中运行此命令时,我绝对不会出错。但是当我将此命令放入脚本并将其作为脚本运行时,我得到了 tr 指示的 Broken pipe 错误。我已经阅读了几个相关主题,但仍然没有答案为什么脚本和交互行为不同,有没有办法通过 shell 选项或其他东西来控制它?

编辑我:

关于给定的 cmets,我发现指示损坏的管道错误可以通过以下方式控制:

 trap - SIGPIPE # to ignore errors

 trap "" SIGPIPE # to display errors

编辑二:

好吧,我提供的有关繁殖条件的信息不正确。最后,似乎是使用 os.system() 调用脚本的 python 包装器引起的问题:

 python -c "import os; os.system('sh -c \"< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c8\"')"

给定的行会产生与使用的操作系统无关的损坏管道错误。

编辑 III:

这里已经讨论过这个话题: https://mail.python.org/pipermail/python-dev/2005-September/056341.html

【问题讨论】:

听起来像是here 和参考问题中正在讨论的问题。 (这里不涉及shellcheck,它恰好是对这个确切问题的讨论和关于拥有shellcheck的讨论,这是一个很棒的工具,警告这种事情。) 在 Cygwin/bash-4.1.10(4) 下完美运行...(在脚本中设置你的行加上 echo $var,除了 #!/bin/sh。) 你是否在脚本中捕获了 SIGPIPE? trap - pipe == 将 sigpipe 重置为其默认配置(=kill 目标); trap '' pipe == 设置 sigpipe 的配置为 SIG_IGN tr -dc _A-Z-a-z-0-9 显然应该是tr -dc _A-Za-z0-9。我得到“无效的字节序列”,除非我在 repro 命令行前加上 LC_ALL=C (来自urandom 的字节通常不是有效的 UTF-8,它会触发 tr 和通常在 UTF-8 语言环境中的大多数字符处理实用程序)。 【参考方案1】:

如果其中一个父进程捕获sigpipe,则管道将继承ignore 信号处置,这将导致您遇到此问题。

这可以(安全地)复制:

( trap '' pipe; var=`< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c8` )

通常,head -c8 命令将很快完成,此时其stdin 将关闭。因为它的stdin 是一个连接到trstdout 的管道,所以现在tr 写入它的stdout 不再有意义。一旦它尝试,系统将使用SIGPIPE 杀死它。 除非tr 忽略此信号或从其父级继承此信号的ignore (SIG_IGN) 处置。然后writetr 的损坏stdout 只会导致常规错误并将errno 设置为EPIPE,此时tr 很可能会字符串化并将此错误输出到它的stderr 和退出。

【讨论】:

【参考方案2】:

This answer 很好地总结了从 Python 到 head 的管道问题,并展示了一些解决方法。

https://***.com/a/30091579/456550

【讨论】:

【参考方案3】:

问题似乎是head 从输入流中读取指定(或默认)行数,打印它们,然后退出。因此,仍在写入的管道中的上游程序会发现输出流已关闭。在我看来,这是head 本身设计的一个限制。你可以改用sed,它读取整个流:sed -n "1,10p"相当于head -n10

【讨论】:

以上是关于管道到头部导致从 python 调用的 shell 脚本中的管道损坏的主要内容,如果未能解决你的问题,请参考以下文章

python---从尾到头打印链表

剑指offer从尾到头打印链表python

从 java 程序调用 unix shell?从 java 程序中向 unix 管道读取和写入稳定的数据流

为啥我的 Python3 脚本不愿将其输出通过管道传输到 head 或 tail(sys 模块)?

Python中带有管道的Shell命令[重复]

shell 管道导致的变量重置问题