一个简单的例子,我试图解决所有情况。 我正在运行一个subprocess来执行某个任务,我不希望它要求stdin,但是在极less数情况下,我可能没有想到,它可能会尝试读取。 我想阻止它挂在那种情况下。
这里是一个典型的例子:
import subprocess p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"]) p.wait()
这将永远挂起。 我已经尝试添加
stdin=open(os.devnull)
这样..
如果我find一个有价值的解决scheme将发布 对于我来说在父进程中接受一个例外就足够了 – 而不是无休止地等待沟通/等待。
更新:似乎问题可能比我最初期望的更复杂,subprocess(在密码和其他情况下)从其他文件描述符中读取 – 例如与/ dev / tty进行交互的shell。 可能不像我想的那样容易解决..
显然罪魁祸首是直接使用/ dev / tty等。
至少在Linux上,一个解决方案是添加到Popen调用下面的参数:
preexec_fn=os.setsid
这会导致新的会话ID被设置,并且不允许直接从tty读取。 我可能会使用下面的代码(stdin关闭以防万一):
import subprocess import os p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"], stdin=subprocess.PIPE, preexec_fn=os.setsid) p.stdin.close() #just in case p.wait()
最后两行可以被一个电话替换:
p.communicate()
因为沟通()在发送所有提供的输入后关闭stdin文件。
看起来简单而优雅。
或者:
import subprocess import os p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"], stdin=open(os.devnull), preexec_fn=os.setsid) p.communicate()
如果您的子进程可能要求输入密码,那么可以在标准输入/输出/错误流之外执行,如果有可用的tty,请参阅Q中的第一个原因:为什么不使用管道(popen())?
正如你已经注意到的 ,创建一个新的会话阻止子ask-password.py
使用父母的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
调用,这样它就不会挂起等待密码,你可以使用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
。
为了模拟Python 2中的DEVNULL=open(os.devnull, 'r+b', 0)
,你可以使用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()
否则不需要.communicate()
。 如果使用具有真实文件描述符( .fileno()
)的对象.fileno()
例如open(os.devnull, ..)
返回,则check_call()
是完全安全的。 重定向发生在子进程执行之前(在fork()
,在exec()
之前) – 没有理由在这里使用check_call()
而不是check_call()
。