Python 子进程模块,如何向管道命令系列中的第一个提供输入?

发布于 2024-10-18 17:39:08 字数 1739 浏览 2 评论 0原文

我正在尝试使用Python的子进程模块。我需要的是将输入发送到第一个进程,其输出成为第二个进程的输入。 情况基本上和这里文档中给出的例子几乎一样: http://docs.python.org/library/subprocess.html#replacing -shell-管道 除了我需要提供第一个命令的输入。 这是复制的示例:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

如果我们将第一行更改为:

p1 = Popen(["cat"], stdout=PIPE, stdin=PIPE)

如何向进程提供输入字符串? 如果我尝试将最后一行更改为:

output = p2.communicate(input=inputstring)[0]

这不起作用。

我确实有一个工作版本,它仅将第一个命令的输出存储在字符串中,然后将其传递给第二个命令。这并不可怕,因为基本上没有可以利用的并发性(在我的实际用例中,第一个命令将很快退出并在最后生成所有输出)。 这是完整的工作版本:

import subprocess

simple = """Writing some text
with some lines in which the
word line occurs but others
where it does
not
"""

def run ():
  catcommand = [ "cat" ]
  catprocess = subprocess.Popen(catcommand,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
  (catout, caterr) = catprocess.communicate(input=simple)
  grepcommand = [ "grep", "line" ]
  grepprocess = subprocess.Popen(grepcommand,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
  (grepout, greperr) = grepprocess.communicate(input=catout)
  print "--- output ----"
  print grepout 
  print "--- error ----"
  print greperr 

if __name__ == "__main__":
  run()

我希望我已经足够清楚了,感谢您的帮助。

I am trying to use Python's subprocess module. What I require is to send input to the first process whose output becomes the input of the second process.
The situation is basically almost the same as the example given in the documentation here:
http://docs.python.org/library/subprocess.html#replacing-shell-pipeline
except that I need to provide input the first command.
Here is that example copied:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

If we change the first line to:

p1 = Popen(["cat"], stdout=PIPE, stdin=PIPE)

How do I provide the input string to the process?
If I attempt it by changing the final line to:

output = p2.communicate(input=inputstring)[0]

This doesn't work.

I do have a working version, which just stores the output of the first command in a string and then passes that to the second command. This isn't terrible as there is essentially no concurrency that can be exploited (in my actual use case the first command will exit rather quickly and produce all of its output at the end).
Here is the working version in full:

import subprocess

simple = """Writing some text
with some lines in which the
word line occurs but others
where it does
not
"""

def run ():
  catcommand = [ "cat" ]
  catprocess = subprocess.Popen(catcommand,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
  (catout, caterr) = catprocess.communicate(input=simple)
  grepcommand = [ "grep", "line" ]
  grepprocess = subprocess.Popen(grepcommand,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
  (grepout, greperr) = grepprocess.communicate(input=catout)
  print "--- output ----"
  print grepout 
  print "--- error ----"
  print greperr 

if __name__ == "__main__":
  run()

I hope I've been clear enough, thanks for any help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

尛丟丟 2024-10-25 17:39:08

如果您这样做,

from subprocess import Popen, PIPE
p1 = Popen(["cat"], stdout=PIPE, stdin=PIPE)

您应该执行 p1.communicate("Your Input to the p1") ,这将流经 PIPE。
标准输入是进程的输入,您应该仅与其进行通信。

给出的程序绝对没问题,看起来没有问题。

If you do

from subprocess import Popen, PIPE
p1 = Popen(["cat"], stdout=PIPE, stdin=PIPE)

You should do p1.communicate("Your Input to the p1") and that will flow through the PIPE.
The stdin is the process's input and you should communicate to that only.

The program which have given is absolutely fine, there seems no problem with that.

等风来 2024-10-25 17:39:08

我假设 cat 、 grep 只是示例命令,否则您可以使用没有子进程的纯 Python 解决方案,例如:

for line in simple.splitlines():
    if "line" in line:
       print(line)

或者如果您想使用 grep :

from subprocess import Popen, PIPE

output = Popen(['grep', 'line'], stdin=PIPE, stdout=PIPE).communicate(simple)[0]
print output,

您可以将第一个命令的输出传递给第二个命令,而无需先将其存储在字符串中:

from subprocess import Popen, PIPE
from threading import Thread

# start commands in parallel
first = Popen(first_command, stdin=PIPE, stdout=PIPE)
second = Popen(second_command, stdin=first.stdout, stdout=PIPE)
first.stdout.close() # notify `first` if `second` exits 
first.stdout = None # avoid I/O on it in `.communicate()`

# feed input to the first command
Thread(target=first.communicate, args=[simple]).start() # avoid blocking

# get output from the second command at the same time
output = second.communicate()[0]
print output,

如果您不想将所有输入/输出存储在内存中;您可能需要线程(以块的方式读取/写入而不阻塞)或选择循环(适用于 POSIX)。

如果有多个命令,则按照 @Troels Folke 的建议直接使用 shell 或使用 < a href="https://stackoverflow.com/a/16709666/4279">一个库,例如 plumbum,它隐藏了手动模拟 shell 的所有血腥细节。

I assume that cat, grep are just example commands otherwise you could use a pure Python solution without subprocesses e.g.:

for line in simple.splitlines():
    if "line" in line:
       print(line)

Or if you want to use grep:

from subprocess import Popen, PIPE

output = Popen(['grep', 'line'], stdin=PIPE, stdout=PIPE).communicate(simple)[0]
print output,

You can pass the output of the first command to the second one without storing it in a string first:

from subprocess import Popen, PIPE
from threading import Thread

# start commands in parallel
first = Popen(first_command, stdin=PIPE, stdout=PIPE)
second = Popen(second_command, stdin=first.stdout, stdout=PIPE)
first.stdout.close() # notify `first` if `second` exits 
first.stdout = None # avoid I/O on it in `.communicate()`

# feed input to the first command
Thread(target=first.communicate, args=[simple]).start() # avoid blocking

# get output from the second command at the same time
output = second.communicate()[0]
print output,

If you don't want to store all input/output in memory; you might need threads (to read/write in chunks without blocking) or a select loop (works on POSIX).

If there are multiple commands, it might be more readable just to use the shell directly as suggested by @Troels Folke or use a library such as plumbum that hides all the gory details of emulating the shell by hand.

久伴你 2024-10-25 17:39:08

嗯,为什么不加入一点(ba)sh呢? :-)

from subprocess import Popen, PIPE
cproc = Popen('cat | grep line', stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
out, err = cproc.communicate("this line has the word line in it")

但请注意:

  • 这只适用于使用 Bourne Shell 兼容 shell 的系统(如大多数 *nix'es)

  • Usign shell=True 并将用户输入放入命令字符串中是一个坏主意,除非您首先转义用户输入。阅读子流程文档 -> “常用参数”了解详细信息。

  • 这是丑陋的,不可移植的,非Pythonic的等等......

编辑:
不过,如果您只想使用 grep,则无需使用 cat。只需将输入直接提供给 grep,或者更好的是,使用 python 正则表达式。

Hmm, why not mix in a bit of (ba)sh? :-)

from subprocess import Popen, PIPE
cproc = Popen('cat | grep line', stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True)
out, err = cproc.communicate("this line has the word line in it")

BEWARE though:

  • This only works on systems that use a Bourne Shell compatible shell (like most *nix'es)

  • Usign shell=True and putting user input in the command string is a bad idea, unless you escape the user input first. Read the subprocess docs -> "Frequently Used Arguments" for details.

  • This is ugly, non portable, non pythonic and so on...

EDIT:
There is no need to use cat though, if all you want to do is grep. Just feed the input directly to grep, or even better, use python regular expressions.

谜泪 2024-10-25 17:39:08

我遇到了类似的问题,我想要几个管道Popen
这对我有用:

from subprocess import Popen, PIPE

p1 = Popen(["cat"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdin.write(inputstring)
p1.stdin.close()
output = p2.communicate()[0]

但是,带有线程的 @jfs 解决方案 似乎更强大。

I faced a similar problem where I wanted to have several piped Popen.
This worked for me:

from subprocess import Popen, PIPE

p1 = Popen(["cat"], stdin=PIPE, stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdin.write(inputstring)
p1.stdin.close()
output = p2.communicate()[0]

However, the @jfs solution with threads seems more robust.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文