Popen
可能不适合交互式程序,因为存在缓冲问题和某些程序直接从终端读写,例如检索密码。参见为什么不能只使用管道(popen())?。
如果您想模拟{{link4:script
实用程序}},则可以使用{{link5:pty.spawn()
}},请参见从Python子进程复制终端输出的代码示例或记录语法错误和未捕获的异常以供Python子进程使用,并将它们打印到终端上:
import os
import pty
import sys
with open('log', 'ab') as file:
def read(fd):
data = os.read(fd, 1024)
file.write(data)
file.flush()
return data
pty.spawn([sys.executable, "test.py"], read)
或者您可以使用 pexpect
来获得更多的灵活性:
import sys
import pexpect
with open('log', 'ab') as fout:
p = pexpect.spawn("python test.py")
p.logfile = fout
p.interact()
如果您的子进程不缓冲其输出(或者它不影响交互性),并且将其输出打印到其stdout或stderr,则可以尝试使用
subprocess
:
import sys
from subprocess import Popen, PIPE, STDOUT
with open('log','ab') as file:
p = Popen([sys.executable, '-u', 'test.py'],
stdout=PIPE, stderr=STDOUT,
close_fds=True,
bufsize=0)
for c in iter(lambda: p.stdout.read(1), ''):
for f in [sys.stdout, file]:
f.write(c)
f.flush()
p.stdout.close()
rc = p.wait()
要分别读取stdout/stderr,您可以使用Python subprocess get children's output to file and terminal?中的teed_call()
。
shell=True
的原因是什么?特别是当你要运行的东西本身就是/bin/bash
的时候?你基本上是告诉Python启动一个bash
的副本来告诉它启动另一个bash
的副本(可能是这样,因为你没有显示整个代码),来告诉它启动一个adduser
的副本。你可能甚至不需要一个shell,更何况两个了。 - abarnertexecutable
不会启动一个新的bash
进程:它告诉subprocess
模块使用其值,而不是/bin/sh
,如果shell=True
。args
可能是一些带有bash-isms的shell命令,例如'use_tty &>/dev/null'
。据我所知,意图是:script -c '/bin/bash -c "$args"' log
。尽管正如我的答案所示,您甚至不需要额外的bash进程来运行此处的命令。 - jfs