如何使用popen将stdout管道传输到另一个程序?

0 投票
2 回答
1382 浏览
提问于 2025-04-18 02:37

抱歉如果这个问题之前有人问过,我在网上查了很多资料,但还是没找到答案。

我正在写一个脚本,其中有一系列系统调用,其中一个调用的形式如下:

cat file | /some/program o > output.txt

简单来说,就是把一个文件的内容输出到标准输出,然后通过管道传给某个程序,程序处理后再把结果输出到另一个文件。不过在这个情况下,使用的 /some/program 不是很灵活,我必须先用 cat 命令把文件内容传给它,然后再用参数 o > some_out_file 来使用它。

我尝试把那一行的 shlex.split() 结果传给 popen() 的参数,但这样只会打印出文件名、/some/program 的二进制文件名,以及如果存在的话 output.txt,这显然不是我想要的结果。

我对使用 Python 的这一部分还比较陌生,所以如果答案很明显我也很抱歉。如果有其他方法可以进行这个系统调用,而不是尝试使用 subprocess.popen() 或类似的方式,我也很乐意接受任何建议,感谢大家的帮助!

另外,我也可以直接用 os.system(...) 来处理这个,但为了和脚本的其他部分保持一致,我不想在这种情况下使用特定的异常。

2 个回答

0

要在Python中模拟 < file /some/program o > output.txt 这个命令,你可以这样做:

from subprocess import check_call

with open('file', 'rb', 0) as file, open('output.txt', 'wb', 0) as output_file:
    check_call(['/some/program', 'o'], stdin=file, stdout=output_file)

关于标题中的问题,你可以参考文档中的“替换shell管道”示例

from subprocess import Popen, PIPE

# cat file | /some/program o > output.txt
p1 = Popen(["cat", "file"], stdout=PIPE)
with open('output.txt', 'wb', 0) as output_file:
    p2 = Popen(["/some/program", "o"], stdin=p1.stdout, stdout=output_file)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
p1.wait()
p2.wait()

如果这个命令来自一个可信的输入,你可以使用 shell=True 来创建一个管道:

check_call("/bin/a b c | /bin/d 'e f'", shell=True)
0

这就是你想要的内容吗?

Popen.communicate

这个功能可以让你和一个正在运行的程序互动:你可以往它的输入里发送数据,同时也能从它的输出和错误输出中读取数据,直到数据读完为止。最后,它会等这个程序结束。你可以选择性地给它一个字符串作为输入,如果不想发送数据,就可以用None。

这和执行 cat file | head -n 10 > out.txt 的效果很像。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import subprocess

program="head"
args=["-n", "10"]
popen_args = [program] + args #["head", "-n", "10"]

p = subprocess.Popen(popen_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# sample stdin
stdin = "\n".join(["line %s" % x for x in xrange(0, 100)])
out, err = p.communicate(stdin)

# save to file
with open('out.txt', 'w') as f: f.write(out)

撰写回答