如果您的子进程可能要求输入密码,那么如果 tty 可用,它可能会在标准输入/输出/错误流之外进行,请参阅Q: Why not just use a pipe (popen())? 中的第一个原因
作为you've noticed,创建新会话会阻止子进程使用父进程的 tty,例如,如果您有 ask-password.py 脚本:
#!/usr/bin/env python
"""Ask for password. It defaults to working with a terminal directly."""
from getpass import getpass
try:
_ = getpass()
except EOFError:
pass # ignore
else:
assert 0
然后将其作为子进程调用,以便它不会挂起等待密码,您可以使用start_new_session=True 参数:
#!/usr/bin/env python3
import subprocess
import sys
subprocess.check_call([sys.executable, 'ask-password.py'],
stdin=subprocess.DEVNULL, start_new_session=True,
stderr=subprocess.DEVNULL)
stderr 也被重定向到这里,因为getpass() 使用它作为后备,打印警告和提示。
要在 Python 2 上模拟 Unix 上的 start_new_session=True,您可以使用 preexec_fn=os.setsid。
To emulate subprocess.DEVNULL on Python 2, you could use DEVNULL=open(os.devnull, 'r+b', 0) 或传递stdin=PIPE 并立即使用.communicate() 关闭它:
#!/usr/bin/env python2
import os
import sys
from subprocess import Popen, PIPE
Popen([sys.executable, 'ask-password.py'],
stdin=PIPE, preexec_fn=os.setsid,
stderr=PIPE).communicate() #NOTE: assume small output on stderr
注意:你不需要.communicate(),除非你使用subprocess.PIPE。如果您使用具有真实文件描述符 (.fileno()) 的对象,例如 open(os.devnull, ..) 返回的对象,check_call() 是非常安全的。重定向发生在子进程执行之前(在fork() 之后,exec() 之前)——这里没有理由使用.communicate() 而不是check_call()。