繁体   English   中英

Python subprocess 模块,我如何为管道命令系列中的第一个提供输入?

[英]Python subprocess module, how do I give input to the first of series of piped commands?

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

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()

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

如果你这样做

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

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

给出的程序绝对没问题,似乎没有问题。

我假设catgrep只是示例命令,否则您可以使用没有子进程的纯 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 或使用诸如plumbum之类的库来隐藏手动模拟 shell 的所有血腥细节可能更具可读性。

嗯,为什么不混合一点(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 并将用户输入放在命令字符串中是一个坏主意,除非您先转义用户输入。 阅读 subprocess docs -> "Frequently Used Arguments" 了解详细信息。

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

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

我遇到了类似的问题,我想要几个管道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解决方案似乎更健壮。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM