我正在尝试使用子进程执行系统命令并读取输出。
但如果命令超过10秒,我想杀死子进程。
我尝试过多种方式。
我的上一次尝试受到了这篇文章的启发:https://stackoverflow.com/a/3326559/969208
示例:
import os
import signal
from subprocess import Popen, PIPE
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
def pexec(args):
p = Popen(args, stdout=PIPE, stderr=PIPE)
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(10)
stdout = stderr = ''
try:
stdout, stderr = p.communicate()
signal.alarm(0)
except Alarm:
try:
os.kill(p.pid, signal.SIGKILL)
except:
pass
return (stdout, stderr)
问题是:程序退出后,cli中没有显示字符,直到我返回。而回击不会给我一个新的界限。
我想这与stdout和stderr管道有关。
我尝试过冲洗并从管道读取(p.stdout.flush())
我也尝试过不同的Popen args,但可能会错过一些东西。我以为我会在这里保持简单。
我在Debian服务器上运行它。
我在这里错过了什么吗?
编辑:
似乎这只是杀死正在进行的ffmpeg进程的情况。如果ffmpeg进程在10秒之前正常退出,则完全没有问题。
我尝试过执行一些耗时超过10秒的不同命令,一个打印输出,一个不输出,一个ffmpeg命令检查文件的完整性。
args = ['sleep', '12s'] # Works fine
args = ['ls', '-R', '/var'] # Works fine, prints lots for a long time
args = ['ffmpeg', '-v', '1', '-i', 'large_file.mov','-f', 'null', '-'] # Breaks cli output
我相信ffmpeg使用\ r进行打印并在strerr管道上打印所有内容。这可能是原因吗?任何想法如何解决它?
答案 0 :(得分:1)
好。你的代码肯定可以在我的Ubuntu服务器上正常工作。
(我认为这是Debian的近亲或兄弟)
我添加了几行,以便我可以测试你的代码。
import os
import signal
from subprocess import Popen, PIPE
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
def pexec(args):
p = Popen(args, stdout=PIPE, stderr=PIPE)
signal.signal(signal.SIGALRM, alarm_handler)
signal.alarm(1)
stderr = ''
try:
stdout, stderr = p.communicate()
signal.alarm(0)
except Alarm:
print "Done!"
try:
os.kill(p.pid, signal.SIGKILL)
except:
pass
return (stdout, stderr)
args = ('find', '/', '-name','*')
stdout = pexec(args)
print "----------------------result--------------------------"
print stdout
print "----------------------result--------------------------"
像魅力一样。
如果此代码适用于您的服务器,我猜问题实际上就在于
您尝试检索数据的命令行应用程序。
答案 1 :(得分:0)
我有同样的问题。我无法从python子进程中正常终止正在运行的FFmpeg,因此我正在使用<process>.kill()
。但是我认为这意味着FFmpeg不能正确恢复tty的模式(如下所述:https://askubuntu.com/a/172747)
您可以在bash提示符处运行reset
来恢复您的shell,但这会清除屏幕,以便您在继续工作时看不到脚本的输出。
最好是运行stty echo
,这会为你的shell会话重新开启回声。
您甚至可以在使用FFmpeg后在脚本中运行此功能。我在做:
ffmpeg_popen.kill()
ffmpeg_popen.wait()
subprocess.call(["stty", "echo"])
这适用于Ubuntu,bash作为我的shell。 YMMV,但我希望它有所帮助。它闻起来很丑,但它是我发现的最佳解决方案。
答案 2 :(得分:0)
我遇到了与ffmpeg类似的问题。似乎如果使用Popen.kill()杀死ffmpeg,它将无法正常关闭并且不会在终端上恢复回显。
我们可以使用管道到stdin来解决这个问题,并像在cli会话中那样写q来关闭ffmpeg:
p = Popen(args, stdin=PIPE stdout=PIPE, stderr=PIPE)
p.stdin.write(b"q")
最好使用Popen.communicate以避免死锁。以下内容也适用:
p = Popen(args, stdin=PIPE stdout=PIPE, stderr=PIPE)
p.communicate(b'q')
但是看起来甚至是以下作品:
p = Popen(args, stdin=PIPE stdout=PIPE, stderr=PIPE)
p.kill()
我不确定是什么原因导致这个ffmpeg干净地关闭它,如果它有一个输入管道。也许它首先与导致这个错误的原因有关?