【问题标题】:cat blocking when called via subprocess.Popen()通过 subprocess.Popen() 调用时 cat 阻塞
【发布时间】:2018-01-27 13:10:00
【问题描述】:

当通过 subprocess.Popen() 调用 linux 猫时,我遇到了意想不到的行为。

Python 脚本的结构如下:

import os, subprocess

def _degrade_child_rights(user_uid, user_gid):
    def result():
        os.setgid(user_gid)
        os.setegid(user_gid)
        os.setuid(user_uid)
        os.seteuid(user_uid)
    return result

child = subprocess.Popen("cat /home/myuser/myfolder/screenlog.0", 
preexec_fn=_degrade_child_rights(0, 0), shell=True, 
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

当我用 ps aux | 检查执行的 shell 命令时grep cat 它显示 python 成功运行了 shell 命令。

> ps aux | grep cat
root     21236  0.0  0.0   6564   780 pts/1    S    20:49   0:00 /bin/sh -c cat /home/myuser/myfolder/screenlog.0
root     21237  0.0  0.0  11056   732 pts/1    S    20:49   0:00 cat /home/myuser/myfolder/screenlog.0
root     21476  0.0  0.0  15800   936 pts/1    S+   20:52   0:00 grep --color=auto cat

但是,cat 命令永远不会结束。

我还将 cat $file 命令外包给了一个 bash 脚本。然后 bash 执行我的 cat-call,但也会阻塞。

当我手动执行 cat $file 时,它​​会按预期运行,因此文件末尾不存在的 EOF 也是不可能的。

我认为,Popen 添加的“/bin/sh -c”会以某种方式与 cat $file 的正确执行混淆。

我能以某种方式阻止这种情况吗?

【问题讨论】:

  • 您似乎没有从 cat 标准输出连接到的子进程管道中读取。这意味着一旦管道的缓冲区已满,它将阻塞。您需要从管道中读取数据,或者将子进程的标准输出连接到 Python 进程的标准输出。
  • 不,sh -c 与问题无关。传递shell=True 告诉Python 你想实际运行sh -c "cat /home/myuser/myfolder/screenlog.0"——如果你不想想要那样,你会想要关闭shell=True 参数,并修改其余的命令来表示一个有效的 argv 列表。引号在ps 的输出中不可见,因为ps 没有以确保其输出可作为命令输入重复使用的方式指定;它在视觉上无法区分句法空间和文字空间。
  • (所以,你不能在ps 输出中区分./myprog "hello world"./myprog hello world;如果你想要 actual argv,最好的做法是将其从 procfs 中拉出,它以 NUL 分隔的形式提供)。
  • @Magnus Reftel 你是对的,这就是问题所在。奥斯汀耶茨你的答案是正确的,但不适合我的需要。我的方法是在 Popen() 调用之后立即开始的线程中通过 readline() 从管道中读取。启动的线程然后将 readline 的输出放到一个队列中,该队列支持超时。

标签: python linux bash python-2.7 shell


【解决方案1】:

您可能想尝试 Popen 对象的通信方法:

Popen.communicate(input=None) 与进程交互:发送数据到 标准输入。从 stdout 和 stderr 读取数据,直到到达文件结尾。 等待进程终止。可选的输入参数应该是 要发送到子进程的字符串,如果不应该有数据,则为 None 发送给孩子。

communicate() 返回一个元组(stdoutdata,stderrdata)。

注意,如果你想发送数据到进程的标准输入,你需要 使用 stdin=PIPE 创建 Popen 对象。同样,得到任何东西 除了结果元组中的 None 之外,您需要给出 stdout=PIPE 和/或 stderr=PIPE。

注意读取的数据是缓存在内存中的,如果有以下情况请不要使用此方法 数据量很大或无限制。

subprocess python doc有更多信息

【讨论】:

    猜你喜欢
    • 2023-03-14
    • 2013-11-16
    • 2015-12-06
    • 1970-01-01
    • 2021-01-20
    • 2015-11-12
    • 1970-01-01
    • 2014-03-23
    • 1970-01-01
    相关资源
    最近更新 更多