【问题标题】:Python script using subprocess and xclip hangs if piped in bash如果在 bash 中通过管道传输,使用子进程和 xclip 的 Python 脚本会挂起
【发布时间】:2017-11-17 02:57:37
【问题描述】:

我有一个 python 脚本,它需要将一些值输出到标准输入并将另一个字符串复制到剪贴板。我正在使用模块subprocess 通过Popen 执行xclip 实用程序,如下所示:

# clip.py
import subprocess
from subprocess import Popen

print('PRINT_ME')
p1 = Popen(['xclip', '-selection', 'clipboard'], stdin=subprocess.PIPE)
p1.communicate(input=('PASTE_ME'.encode()))

脚本按预期工作:PRINT_ME 在 bash 中回显,PASTE_ME 可以粘贴,并立即返回。

将脚本输出通过管道传输到另一个命令时会出现问题。假设一个人想使用tee 重定向到一个文件和标准输入:

$ python clip.py|tee file.txt

程序按预期工作但不返回,即 shell 不交还控制权。

如何解决这个问题?

一些重要信息xclip 实用程序 forks 本身(以应对 X 上剪贴板的实现)在 shell 中使用时维护可复制的字符串立即返回(它分叉到后台)。似乎外壳将clip.pyxclip stdin/stdout 都附加到tee
如果使用ps u 找到xclip -selection clipboard 并杀死命令返回。

我使用的是 Python 3.4。
谢谢

【问题讨论】:

    标签: python shell subprocess piping xclip


    【解决方案1】:

    这不是由于 xclip 的分叉,而是由于 Python 处理 Popen.wait() 的方式(通过您对 communicate() 的调用调用) - 从 Python 的角度来看,xclip(默认为静音)没有关闭它的流,所以它等待......这就是为什么除了 Python 在将其流传输到其他东西(在这种情况下为tee)时,除了 Python 移动到您的 p1.communicate() 行之外,一切正常 - 它等待其所有文件句柄关闭.. .

    您可以手动打开和关闭您的流,或者只配置 xclip 以将 STDIN 过滤为 STDOUT 并让 Python 满意:

    import subprocess
    
    p1 = subprocess.Popen(['xclip', '-selection', 'clipboard', '-f'], stdin=subprocess.PIPE)
    p1.communicate(input=('PASTE_ME'.encode()))
    # etc.
    

    没有测试它,但它应该可以工作。如果您不希望 xclip 打印到您当前的 STDOUT,只需在将 subprocess.Popen() 实例化为 None(或 Python 3.x 的 subprocess.DEVNULL)或您想要的任何其他流句柄时将其重定向。

    【讨论】:

    • 感谢您的回答,两种方式都很好!我已经测试了各种修改并发现有趣的是,p1.communicate 之后的任何代码(在原始缺陷代码中)都被执行,程序停止退出。从提出的两个解决方案中,哪个更“pythonic”且无副作用?
    • 你说得对,我表达得很糟糕 - Popen 为 xclip 的 STDOUT 打开一个 FH(并将其绑定到当前的 STDOUT),并且由于 xclip 从不关闭它一直在等待的流end 所以它可以干净地退出子进程。至于 Pythonic - 这是一个虚幻的术语,对不同的人意味着不同的事物,但大多数人倾向于同意 KISS 方法是 Pythonic 的缩影 - 因此,除非您需要使用 STDIN/STDOUT 做一些花哨的事情,只需使用 Popen.communicate() 并在 xclip 已经允许您设置它以与 Python 很好地配合使用时省去一些麻烦。