1

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

但是如果命令需要超过 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 参数,但可能错过了一些东西。只是想我会在这里保持简单。

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

4

3 回答 3

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--------------------------"

奇迹般有效。

如果此代码在您的服务器上有效,我想问题实际上在于

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

于 2012-05-14T15:33:57.397 回答
0

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

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

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

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

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

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

于 2016-03-17T21:55:28.880 回答
0

我遇到了与 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 干净地关闭。也许它首先与导致此错误的原因有关?

于 2017-09-27T00:44:45.123 回答