如何解决subprocess.Popen with shell=True 挂在 'time <...>'
我正在使用这段代码,它应该是python2到python3移植阶段的过渡(我知道run
有第三方库,我想实现自己的来收集经验)
def run(args,stdin=None,input=None,stdout=None,stderr=None,capture_output=False,timeout=None,encoding=None,**popen_kwargs):
# create stderr and stdout pipes,if capture_output is true
if capture_output:
_stderr = subprocess.PIPE
_stdout = subprocess.PIPE
else:
_stdout,_stderr = stdout,stderr
# if input is given,create stdin as pipe,where input will be passed into
if input is not None:
_stdin = subprocess.PIPE
else:
_stdin = stdin
# this starts the process. python2 did not have 'encoding'
if sys.version_info.major >= 3:
proc = subprocess.Popen(args,stdin=_stdin,stdout=_stdout,stderr=_stderr,encoding=encoding,**popen_kwargs)
else:
proc = subprocess.Popen(
args,**popen_kwargs)
# run a background timer to interrupt 'communicate',if necessary
if timeout is not None:
def cancel():
try:
proc.terminate()
except OSError:
# an exception here means that the process is gone already
pass
cancel_timer = Timer(timeout,cancel)
cancel_timer.start()
# special case for python2 for which we allow passing 'unicode' if an encoding is used
if input is not None and sys.version_info.major < 3:
if type(input) == unicode:
import codecs
input = codecs.encode(input,encoding)
(stdoutoutput,stderroutput) = proc.communicate(input)
# check timeout scenario
if timeout is not None:
if not cancel_timer.is_alive():
raise TimeoutExpired(args,timeout,stdoutoutput,stderroutput)
else:
cancel_timer.cancel()
cancel_timer.join()
# on python2,outputs will always be 'str',which is fine with us,as it's the union of
# str and bytes
return CompletedProcess(args,proc.poll(),stderroutput)
但是,每当我尝试在 python2 和 python3 上使用 shell 内置 communicate
前缀和 time
执行任何操作时,代码都会在 capture_output
内无限期地阻塞。这一定是非常愚蠢的事情。
>>> run("time sleep 5m",shell=True,capture_output=True,timeout=1)
... (^ C to stop it)
>>> run("sleep 5m",timeout=1)
zsubprocess.TimeoutExpired: sleep 5m: Timeout after 1 seconds
我不明白为什么会这样。
解决方法
事实证明,当命令仅为 subprocess
时,sleep 5m
不会不执行 shell,但仅当命令为 time sleep 5m
时(更准确地说: sh
优化了它的 -c
并且显然 exec
它而不是分叉)。
当我执行 proc.terminate()
时,它只终止了包装壳,而不是 sleep
,它成为子子收割者(此处为 init
)的子级。并且可能(我猜)communicate
等待管道产生 EOF 以收集未完成的数据,除非睡眠终止,否则这种情况不会发生。以下解决方案取自 https://stackoverflow.com/a/25134985/34509 。而不是proc.kill
,做
process = psutil.Process(proc.pid)
if proc.poll() is None:
for child in process.children(recursive=True):
child.kill()
process.kill()
我们可能会与进程竞争,它可以在我们杀死它们之前创建新进程,但至少现在简单的情况可以工作!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。