[英]subprocess.stdout.readline() hangs when stderr is redirected to subprocess.STDOUT
I've been looking at other similar questions, but I don't think anyone has covered this particular case.我一直在研究其他类似的问题,但我认为没有人讨论过这个特殊情况。
I've found that using stderr=subprocess.STDOUT in my Popen call causes stdout.readline() to hang forever in rare cases;我发现在我的 Popen 调用中使用stderr= subprocess.STDOUT 会导致 stdout.readline() 在极少数情况下永远挂起; the best example I have is an attempt at an internal SSH command that sets up a tunnel:我最好的例子是尝试使用内部 SSH 命令设置隧道:
ssh -N -f -L 81:<SOME URL>:80 <SOME OTHER URL>
When I try to run this command in Popen with stderr redirected to stdout, it prints the 2 outputs and then hangs on stdout.readline() forever:当我尝试在 Popen 中运行此命令并将 stderr 重定向到 stdout 时,它会打印 2 个输出,然后永远挂在 stdout.readline() 上:
import subprocess as sp
cmd = "ssh -N -f -L 81:<SOME URL>:80 <SOME OTHER URL>"
p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.STDOUT, shell=True, env=os.environ, encoding="utf-8")
while True:
out = p.stdout.readline()
if out:
print(out)
if p.poll() is not None:
break
I'm redirecting stderr to stdout so that I can have both streams in one place.我正在将 stderr 重定向到 stdout,以便我可以将两个流放在一个地方。
Is there a way I can check if there's a line to read BEFORE trying to read it with readline()?有没有办法在尝试使用 readline() 读取之前检查是否有要读取的行?
Also, why doesn't readline() simply return ""
if the process is finished?另外,如果进程完成,为什么 readline() 不简单地返回""
? This seems like a huge miss.这似乎是一个巨大的失误。
To anyone finding this, I worked around this with a (not my own) clever use of signal
.对于任何发现这一点的人,我通过(不是我自己的)巧妙地使用signal
来解决这个问题。
All this does is set up a straight timeout so that when your code runs, you're only giving it N seconds before it has to complete, or you're killing it.所有这一切都是设置一个直接超时,这样当您的代码运行时,您只需在它必须完成之前给它 N 秒,或者您正在杀死它。
This Timeout code has been immensely useful in many places for me;这个超时代码对我来说在很多地方都非常有用; you can import signal
and then straight copy/paste this in to your work:您可以import signal
,然后直接将其复制/粘贴到您的工作中:
# This stuff is so when we get SIGALRM from the timeout functionality we can handle it instead of
# crashing to the ground
class TimeOutError(Exception):
pass
def raise_timeout(var1, var2):
raise TimeOutError
signal.signal(signal.SIGALRM, raise_timeout)
Once you have this set, you're now free to set an alarm with signal.alarm(<SOME SECONDS>)
and do except TimeOutError
in your code;一旦你有了这个设置,你现在可以自由地用signal.alarm(<SOME SECONDS>)
设置一个警报,并在你的代码中做except TimeOutError
; which is what I did for this problem:这就是我为这个问题所做的:
import subprocess as sp
import signal
class TimeOutError(Exception):
pass
def raise_timeout(var1, var2):
raise TimeOutError
signal.signal(signal.SIGALRM, raise_timeout)
cmd = "ssh -N -f -L 81:<SOME URL>:80 <SOME OTHER URL>"
p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.STDOUT, shell=True, env=os.environ, encoding="utf-8")
while True:
out = ""
signal.alarm(1)
try:
out = p.stdout.readline()
except TimeOutError as e:
# Is the process complete?
if p.poll() is not None:
break
else:
pass
# Turn the alarm back off
signal.alarm(0)
if out:
print(out)
if p.poll() is not None:
break
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.