python 2.7 - 与mpg123的子进程控制交互

时间:2013-07-02 01:40:21

标签: python subprocess pty communicate

几个星期前我在这里问了一个与此有关的问题: Python, mpg123 and subprocess not properly using stdin.write or communicate

感谢那里的帮助,我能够做到我当时所需要的。 (没有调用q,但是终止了子进程以阻止它。)## Heading ## 现在虽然我似乎又陷入了困境。

    from subprocess import Popen, PIPE, STDOUT
    p = Popen(["mpg123", "-C", "test.mp3"], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
    #wait a few seconds to enter this, "q" without a newline is how the controls for the player work to quit out if it were ran like "mpg123 -C test.mp3" on the command line
    p.communicate(input='q')[0]
很像以前,我需要这个能够退出mpg123就像它的标准控制一样(比如按'q'退出,或者' - '将音量降低,'+'转动音量等等),现在我使用上面的代码,理论上应该可行,并且它适用于类似的程序。有没有人知道我可以使用子进程使用mpg123中构建的控件(使用“mpg123 -Chatever.mp3”可访问的控件)?终止是不够的,因为我需要控件^ _ ^

编辑:非常感谢abarnert的惊人答案=) 好吧,所以新代码只是abarnert答案的略微修改版本,但mpg123似乎不接受命令

    import os
    import pty
    import sys
    import time

    pid, fd = os.forkpty()
    if pid:
        time.sleep(5)
        os.write(fd, 'b') #this should've restarted the file
        time.sleep(5)
        os.write(fd, 'q') #unfortunately doesn't quit here =(
        time.sleep(5) # quits after this is finished executing
    else:
        os.spawnl(os.P_WAIT, '/usr/bin/mpg123', '-C', 'TEST file.mp3')

2 个答案:

答案 0 :(得分:1)

如果您确实需要控件,则不能只使用Popen

mpg123只有在其stdin是tty时才启用终端控制,而不是它是文件或管道。这就是为什么你在横幅中得到这一行:

Terminal control enabled, press 'h' for listing of keys and functions.

Popen(和subprocess以及它构建的POSIX API)的重点是管道。

那么,你能做些什么呢?


在linux上,您可以使用pty模块。它也可以在其他* nix平台上运行,但它可能不会 - 即使它被构建并包含在你的stdlib中。正如文档所说:

  

因为伪终端处理高度依赖于平台,所以只有Linux代码才能执行。 (Linux代码应该可以在其他平台上运行,但尚未经过测试。)

它肯定在2.7和3.3的* BSD平台上运行,文档中的示例似乎适用于Mac OS X和FreeBSD ......但就我所检查的而言。


与此同时,大多数POSIX平台至少会有os.forkpty,而且这并不难,所以这里有一个简单的程序,播放第一首歌曲中传过的歌曲的前5秒:

import os
import pty
import sys
import time

pid, fd = os.forkpty()
if pid:
    time.sleep(5)
    os.write(fd, 'q')
else:
    os.spawnl(os.P_WAIT, # mode
              '/usr/local/bin/mpg123', # path
              '/usr/local/bin/mpg123', '-C', sys.argv[1]) # args

请注意,我上面使用了os.spawnl。这可能你想要的真实程序;这是出于教学目的,鼓励您阅读文档(以及相应的联机帮助页)并理解这一系列功能。

the docs所述,这不使用PATH环境变量,因此您需要指定程序的完整路径。您可以使用spawnlp代替spawnl来解决此问题。

此外,spawn可能(实际上,虽然文档不完全清楚,但总是这样做)执行另一个fork来执行子项。这确实没有必要,但spawn执行您只需调用exec时需要手动执行的操作。如果您知道自己在做什么,则可能希望使用execl(或execlp)代替spawnl

你可以使用subprocess中的大部分功能,只要你小心(不要创建任何管道,并记住你最终会做 two {{ 1}} s,所以一定要正确设置父/子关系。

另请注意,您需要将路径传递给fork 两次 -once作为路径,然后再将其作为子程序的mpg123传递一次。您也可以第二次通过argv[0]。或者,理想情况下,查看从shell运行它时mpg123所说的内容,然后传递它。无论如何,你必须传递某些作为ps;否则,argv[0]最终成为-C,这意味着mpg123不会认为你给它一个argv[0]标志来启用控制键,而不是你将它重命名为-C并且没有标志运行它......

无论如何,你确实需要阅读文档以了解每个函数的作用,而不仅仅是将它视为你不理解的魔法代码。因此,我故意使用最简单的解决方案来鼓励这一点。


在Windows上,没有-C这样的东西,根本没有办法使用Python内置的工具。您将需要使用各种第三方库之一来控制cmd.exe控制台(也就是DOS提示符)。

答案 1 :(得分:0)

根据abarnert的想法,我们可以打开一个伪终端并将其传递给子进程。

import os
import pty
import subprocess
import time

master, slave = os.openpty()

p = subprocess.Popen(['mpg123', '-C', 'music.mp3'], stdin=master)
time.sleep(3)
os.write(slave, 's')
time.sleep(3)
os.write(slave, 's')
time.sleep(6)
os.write(slave, 'q')