如何将“subprocess”命令与管道一起使用

How to use `subprocess` command with pipes

提问人:zuberuber 提问时间:11/11/2012 最后编辑:martineauzuberuber 更新时间:5/12/2023 访问量:370254

问:

我想与 一起使用。 我尝试了各种解决方案,但到目前为止没有任何效果。有人可以指导我怎么做吗?subprocess.check_output()ps -A | grep 'process_name'

Python Linux 子进程 管道

评论

0赞 jfs 6/16/2013
相关:如何使用子进程。Popen通过管道连接多个进程?
4赞 jfs 6/16/2013
有 psutil 允许以可移植的方式获取进程信息。
0赞 Sridhar Sarnobat 3/14/2022
我希望有一个可靠的紧凑解决方案。

答:

610赞 Taymon 11/11/2012 #1

要在模块中使用管道,必须传递 .subprocessshell=True

但是,由于各种原因,这并不可取,尤其是安全性。相反,请单独创建 and 进程,并将输出从一个进程传递到另一个进程,如下所示:psgrep

ps = subprocess.Popen(('ps', '-A'), stdout=subprocess.PIPE)
output = subprocess.check_output(('grep', 'process_name'), stdin=ps.stdout)
ps.wait()

但是,在您的特定情况下,简单的解决方案是调用,然后调用输出。subprocess.check_output(('ps', '-A'))str.find

评论

137赞 Nicolas 11/12/2012
+1 用于分离输出/输入以避免使用shell=True
10赞 Serge 1/27/2015
别忘了,错误只是意味着 grep 没有发现任何内容,所以这是正常行为。subprocess.CalledProcessError: Command '('grep', 'process_name')' returned non-zero exit status 1
6赞 Papouche Guinslyzinho 8/25/2015
当我们已经有输出时,为什么我们需要 for。 等待子项终止,但子项的内容似乎已放入变量中ps.wait()ps.wait.__doc__output
3赞 Taymon 10/17/2015
@MakisH 您正在查看 ,它已被弃用,取而代之的是(即对象上的方法)。string.findstr.findfindstr
8赞 jfs 3/23/2016
注意:如果过早死亡; 如果它产生足够的输出来填充其 OS 管道缓冲区,则可能会无限期挂起(因为您尚未调用父级)。交换起始顺序,以避免它greppsps.stdout.close()
85赞 jkalivas 11/12/2012 #2

或者,您始终可以在子流程对象上使用 communicate 方法。

cmd = "ps -A|grep 'process_name'"
ps = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
output = ps.communicate()[0]
print(output)

communicate 方法返回标准输出和标准错误的元组。

评论

10赞 Paolo 3/13/2016
我认为使用比.这样的警告:“当使用 stdout=PIPE 和/或 stderr=PIPE 时,这将死锁,并且子进程会为管道生成足够的输出,以便它阻止等待操作系统管道缓冲区接受更多数据。使用 communicate() 来避免这种情况。communicatewait
6赞 EnemyBagJones 12/3/2016
为了澄清 Paolo 上面的评论,警告是等待,而不是沟通——也就是说,这就是他说沟通更好的原因。
0赞 Miguel Ortiz 8/18/2020
python3 中 ps.communicate()[0] 的输出返回一个 bytes 对象。
0赞 tripleee 7/11/2021
你正在重塑,不是太差,而是没有吸引力。正如文档所建议的,当库已经提供了更高级别的函数时,您应该避免使用低级函数,这些函数在一行代码中处理所有这些管道,通常具有更好的边界条件行为。subprocess.check_outputPopen
0赞 tripleee 7/11/2021
为什么要将标准误差重定向到 ?STDOUT
4赞 amoffat 11/12/2012 #3

您可以在 sh.py 中试用管道功能:

import sh
print sh.grep(sh.ps("-ax"), "process_name")

评论

3赞 tripleee 7/11/2021
链接已失效。
0赞 cyraxjoe 3/24/2022
不再了,链接已更新。
3赞 Shooe 11/13/2012 #4

另外,尝试使用命令而不是'pgrep''ps -A | grep 'process_name'

评论

2赞 Shooe 11/13/2012
如果你想要获取进程 ID,显然
31赞 AlcubierreDrive 6/16/2013 #5

请参阅有关使用子进程设置管道的文档:http://docs.python.org/2/library/subprocess.html#replacing-shell-pipeline

我还没有测试过以下代码示例,但它应该大致是您想要的:

query = "process_name"
ps_process = Popen(["ps", "-A"], stdout=PIPE)
grep_process = Popen(["grep", query], stdin=ps_process.stdout, stdout=PIPE)
ps_process.stdout.close()  # Allow ps_process to receive a SIGPIPE if grep_process exits.
output = grep_process.communicate()[0]

评论

2赞 Alvin 12/3/2013
检查失败后,请参阅 Taymon 下面的答案,了解在不乱七八糟的情况下工作的方法
2赞 RightmireM 5/29/2014
subprocess.check_output在 Python 2.6.9 中似乎不存在
2赞 Brent 8/14/2020 #6
command = "ps -A | grep 'process_name'"
output = subprocess.check_output(["bash", "-c", command])

评论

3赞 Charles Duffy 9/4/2020
为什么不使用并让它预置呢?此代码中的任何内容都不需要 bash。(也就是说,完全避免使用 shell 是更好的做法;这个用例是一个合理的用例,但是一旦参数开始参数化——比如将process_name作为参数——安全问题就会出现)。shell=True['sh', '-c']
0赞 Brent 9/4/2020
它很有用,因为你不必拆分字符串,当你引用空格时,这会变得复杂。
1赞 Charles Duffy 9/4/2020
哼? 不需要拆分字符串。 将任何字符串转换为仅包含该字符串的列表 -- 因此,-- 因此,您将被附加到该列表之前,因此您最终会得到 ,正是您的代码在这里所做的,除了 / 差异。subprocess.check_output(command, shell=True)Popen[command]shell=True['sh', '-c']['sh', '-c', command]shbash
1赞 Charles Duffy 9/4/2020
...就此而言,如果您确实尝试将字符串拆分为一个列表并使用 ,则只有该列表的第一个元素将被视为代码;你会得到类似的东西.这不是一件有用的事情:当以这种方式调用时,shell 会以 being 、being 等方式运行......但是由于该命令不查看 、 等,因此所有额外的内容都被简单地忽略了。shell=True['sh', '-c', 'ps', '-A', '|', 'grep', 'process_name']ps$0-A$1|ps$0$1
2赞 Charles Duffy 9/4/2020
如果你阅读 Lib/subprocess.py,你会发现 和 之间没有区别。代码清晰而简单——这不是一个可能隐藏在细节中的魔鬼的地方。subprocess.check_output(["sh", "-c", command])subprocess.check_output(command, shell=True)
43赞 anaken78 11/12/2020 #7

使用 from subprocess.run,您可以将一个命令的输出传递到第二个命令中。input

import subprocess
    
ps = subprocess.run(['ps', '-A'], check=True, capture_output=True)
processNames = subprocess.run(['grep', 'process_name'],
                              input=ps.stdout, capture_output=True)
print(processNames.stdout.decode('utf-8').strip())

评论

6赞 MightyInSpirit 1/26/2021
注意:仅适用于 Python 3.7.9 及更高版本。capture_output
2赞 CervEd 5/6/2021
做什么,目的是什么?checkcapture_output
7赞 tripleee 7/11/2021
@CervEd 这两者都有明确的记录。 是选项组合的简写,如果子进程未返回成功(零)状态,则会引发错误。capture_outputstdout=supprocess.PIPE, stderr=subprocess.PIPEcheck=True
2赞 CervEd 7/11/2021
@tripleee它们被记录在案,在笨拙的 Python 文档中的某个地方,但答案中没有关于为什么包含它们的细节。 例如,不是绝对必要的,但是为了答案起作用。使用这些选项的原因应作为答案的一部分check=Truecapture_output=True
3赞 Eldritch Cheese 8/25/2021
这种方法的缺点是会将进程的所有标准输出读取到内存中。对于像 ps 这样的小程序,这可能没问题,但对于较大的分析管道,应该避免这样做。capture_output
0赞 Raúl Salinas-Monteagudo 5/12/2023 #8

我认为仅仅为了享受流水线而启动一个 shell 并不像它应该的那样优雅。

以下代码使用本机管道支持,它确实有效。subprocess

您可以轻松地对其进行修改,以将两个以上的进程添加到管道中。

#!/usr/bin/env python3

import subprocess


def ps_grep(pattern):
    # First command-line
    ps_command = ["ps", "-A"]

    # Second command-line
    grep_command = ["grep", pattern]

    # Launch first process
    ps_process = subprocess.Popen(ps_command, stdout=subprocess.PIPE)

    # Launch second process and connect it to the first one
    grep_process = subprocess.Popen(
        grep_command, stdin=ps_process.stdout, stdout=subprocess.PIPE
    )

    # Let stream flow between them
    output, _ = grep_process.communicate()

    return output.decode()


if __name__ == "__main__":
    print(ps_grep("python"))