杀死 python ffmpeg 子进程会破坏 cli 输出

Posted

技术标签:

【中文标题】杀死 python ffmpeg 子进程会破坏 cli 输出【英文标题】:Killing python ffmpeg subprocess breaks cli output 【发布时间】:2012-05-22 01:26:39 【问题描述】:

我正在尝试使用子进程执行系统命令并读取输出。

但是如果命令花费超过 10 秒,我想杀死子进程。

我尝试了多种方法。

我最后一次尝试的灵感来自这篇文章:https://***.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 参数,但可能遗漏了一些东西。只是想我会在这里保持简单。

我在 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 管道上打印所有内容。这可能是原因吗?任何想法如何解决它?

【问题讨论】:

【参考方案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--------------------------"

像魅力一样工作。

如果这段代码在你的服务器上运行,我想问题实际上出在

您尝试检索数据的命令行应用程序。

【讨论】:

谢谢瑞恩,你是对的,它工作正常。似乎只有在杀死正在进行的 ffmpeg 进程时才会出现这种情况。请查看我的编辑。【参考方案2】:

我也有同样的问题。我无法让正在运行的 FFmpeg 从 python 子进程正常终止,所以我使用<process>.kill()。但是我认为这意味着 FFmpeg 无法正确恢复 tty 的模式(如此处所述:https://askubuntu.com/a/172747)

您可以通过在 bash 提示符下运行 reset 来恢复您的 shell,但这会清除屏幕,因此您在继续工作时看不到脚本的输出。

最好运行stty echo,它会为您的 shell 会话重新打开回显。

你甚至可以在你 nuked FFmpeg 之后在你的脚本中运行它。我在做:

ffmpeg_popen.kill()
ffmpeg_popen.wait()
subprocess.call(["stty", "echo"])

这适用于我在 Ubuntu 上使用 bash 作为我的 shell。 YMMV,但我希望它有所帮助。它闻起来很老套,但这是我找到的最佳解决方案。

【讨论】:

【参考方案3】:

我在使用 ffmpeg 时遇到了类似的问题。似乎如果使用 Popen.kill() 杀死 ffmpeg,它不会正确关闭并且不会在您的终端上恢复回显。

我们可以使用到标准输入的管道来解决这个问题,并像在 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 干净地关闭。也许它首先与导致此错误的原因有关?

【讨论】:

以上是关于杀死 python ffmpeg 子进程会破坏 cli 输出的主要内容,如果未能解决你的问题,请参考以下文章

当父进程被杀死时,使用 fork() 创建的子进程是不是会自动被杀死?

当节点进程被杀死时杀死所有child_process

当父母在python中崩溃时杀死子进程

如何使用子进程模块杀死(或避免)僵尸进程

Python:当父进程死亡时如何杀死子进程?

Python子进程,定时延迟后杀死进程