Python 如何将字符串传递到subprocess.Popen(使用stdin参数)?

Python 如何将字符串传递到subprocess.Popen(使用stdin参数)?,python,subprocess,stdin,Python,Subprocess,Stdin,如果我这样做: import subprocess from cStringIO import StringIO subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0] 我得到: Traceback (most recent call last): File "<stdin>", line

如果我这样做:

import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0]
我得到:

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 533, in __init__
    (p2cread, p2cwrite,
  File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 830, in _get_handles
    p2cread = stdin.fileno()
AttributeError: 'cStringIO.StringI' object has no attribute 'fileno'
回溯(最近一次呼叫最后一次):
文件“”,第1行,是否在中?
文件“/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py”,第533行,在__
(p2cread,p2cwrite,
文件“/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py”,第830行,位于“获取”句柄中
p2cread=stdin.fileno()
AttributeError:'cStringIO.StringI'对象没有属性'fileno'

显然,cStringIO.StringIO对象与文件duck的距离不够近,无法适应subprocess.Popen。我如何解决这个问题?

我找到了这个解决方法:

>>> p = subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
>>> p.stdin.write(b'one\ntwo\nthree\nfour\nfive\nsix\n') #expects a bytes type object
>>> p.communicate()[0]
'four\nfive\n'
>>> p.stdin.close()
有更好的吗

显然,cStringIO.StringIO对象的嘎嘎声不够接近 适合subprocess.Popen的文件duck

恐怕不行。管道是一个低级操作系统概念,因此它绝对需要一个由操作系统级文件描述符表示的文件对象。您的解决方法是正确的。

文档:

请注意,如果要将数据发送到 这个过程是标准的,你需要 使用创建Popen对象 stdin=管道。类似地,为了得到任何东西 除了结果元组中的“无”之外, 您需要给出stdout=PIPE和/或 stderr=管道也是

更换os.popen*

警告使用communicate()而不是 stdin.write()、stdout.read()或 stderr.read()以避免由于 到任何其他操作系统管道缓冲区 把孩子灌满并堵住 过程

因此,您的示例可以编写如下:

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())
# -> four
# -> five
# ->

在Python 3.5+(3.6+用于
编码
)上,您可以使用,将输入作为字符串传递给外部命令,并在一次调用中获取其退出状态,将其输出作为字符串返回:

#!/usr/bin/env python3
from subprocess import run, PIPE

p = run(['grep', 'f'], stdout=PIPE,
        input='one\ntwo\nthree\nfour\nfive\nsix\n', encoding='ascii')
print(p.returncode)
# -> 0
print(p.stdout)
# -> four
# -> five
# -> 

请注意,如果
s
太大,则
Popen.communicate(input=s)
可能会给您带来麻烦,因为显然父进程会在派生子进程之前对其进行缓冲,这意味着它在该点上需要“两倍”的内存(至少根据“幕后”解释和找到的链接文档).在我的特殊情况下,
s
是一个生成器,它首先被完全扩展,然后才被写入
stdin
,因此在生成子进程之前,父进程非常庞大, 没有留下任何内存来分叉它:

File”/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py“,第1130行,在执行子进程中
self.pid=os.fork()

OSError:[Errno 12]无法分配内存

我正在使用python3,发现您需要对字符串进行编码,然后才能将其传递到stdin:

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
out, err = p.communicate(input='one\ntwo\nthree\nfour\nfive\nsix\n'.encode())
print(out)

我有点惊讶,没有人建议创建管道,在我看来,这是将字符串传递给子流程stdin的最简单的方法:

read, write = os.pipe()
os.write(write, "stdin input here")
os.close(write)

subprocess.check_call(['your-command'], stdin=read)

如果您使用的是Python 3.4或更高版本,则有一个很好的解决方案。请使用
input
参数,而不是接受bytes参数的
stdin
参数:

output = subprocess.check_output(
    ["sed", "s/foo/bar/"],
    input=b"foo",
)

这适用于and,但不适用于and,或者出于某种原因。

在Python 3.7+上执行以下操作:

my_data = "whatever you want\nshould match this f"
subprocess.run(["grep", "f"], text=True, input=my_data)
您可能需要添加
capture\u output=True
以获取作为字符串运行命令的输出

在较早版本的Python上,将
text=True
替换为
universal\u newlines=True

subprocess.run(["grep", "f"], universal_newlines=True, input=my_data)

这对于
grep
来说太过分了,但通过我的旅程,我了解了Linux命令
expect
,以及python库
pexpect

  • :与互动程序对话
  • :用于生成子应用程序、控制它们以及响应其输出中的预期模式的Python模块
import-pexpect
child=pexpect.spawn('grepf',超时=10)
sendline('要匹配的文本')
打印(子项之前)
使用交互式shell应用程序(如
ftp
)与

import-pexpect
child=pexpect.spawn('ftp.openbsd.org')
child.expect('Name.*:')
child.sendline('匿名')
child.expect('密码:')
child.sendline('noah@example.com')
child.expect('ftp>'))
child.sendline('ls/pub/OpenBSD/'))
child.expect('ftp>'))
print child.before#打印ls命令的结果。
child.interact()#将子对象的控制权交给用户。

@Moe:
stdin.write()
不鼓励使用,应使用
p.communicate()
。请参阅我的答案。根据子流程文档:警告-使用communicate()而不是.stdin.write、.stdout.read或.stderr.read,以避免由于任何其他操作系统管道缓冲区填满并阻塞子进程而导致死锁。我认为,如果您确信stdout/err永远不会填满(例如,它将进入一个文件,或者另一个线程正在吃掉它),那么这是一个很好的方法并且您有无限量的数据要发送到stdin。特别是,这样做仍然可以确保stdin是关闭的,因此,如果子进程永远使用输入,则
通信
将关闭管道并允许进程优雅地结束。@Lucretiel,如果进程永远使用stdin,则假定它仍然可以永远编写stdout,因此我们需要全方位的完全不同的技术(无法从中读取()
,就像
communicate()
在没有参数的情况下所做的那样)。我错过了那个警告。我很高兴我问了(尽管我认为我已经找到了答案)。这不是一个好的解决方案。特别是,如果执行此操作,则无法异步处理p.stdout.readline输出,因为您必须等待整个stdout到达。这也是内存效率低下的问题。@OTZ有什么更好的解决方案?@Nick T:“更好”取决于上下文。牛顿定律适用于其适用的领域,但你需要狭义相对论来设计GPS。请参阅。但请注意“如果数据量很大或不受限制,请不要使用此方法”的注释,而不是将其删除以质疑我的答案,我将其添加为注释。。
read, write = os.pipe()
os.write(write, "stdin input here")
os.close(write)

subprocess.check_call(['your-command'], stdin=read)
output = subprocess.check_output(
    ["sed", "s/foo/bar/"],
    input=b"foo",
)
my_data = "whatever you want\nshould match this f"
subprocess.run(["grep", "f"], text=True, input=my_data)
subprocess.run(["grep", "f"], universal_newlines=True, input=my_data)