1

几周前我在这里问了一个与此相关的问题: Python, mpg123 and subprocess not proper using stdin.write or communication

感谢那里的帮助,我能够做我当时需要的事情。(没有调用 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 -C 不管.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')
4

2 回答 2

1

如果你真的需要控件,你不能只使用Popen.

mpg123仅当它的标准输入是 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上面使用过。这可能不是您在实际程序中想要的;出于教学目的,鼓励您阅读文档(和相应的联机帮助页)并了解这一系列功能。

正如文档所解释的,这不使用PATH环境变量,因此您需要指定程序的完整路径。您可以只使用spawnlp而不是spawnl解决此问题。

此外,spawn可能(事实上,总是这样做,尽管文档并不完全清楚)做另一个分叉来执行孩子。这确实没有必要,但spawn如果您只是调用exec. 如果您知道自己在做什么,您可能希望使用execl(或execlp) 而不是spawnl.

subprocess只要小心,您甚至可以使用大部分功能(不要创建任何管道,并记住您最终会做两个 forks,因此请确保正确设置父/子关系)。

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

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


在 Windows 上,没有pty. 您将需要使用各种第三方库之一来控制 cmd.exe 控制台(也称为 DOS 提示符)。

于 2013-07-02T01:50:42.830 回答
1

根据 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')
于 2018-03-28T02:23:28.033 回答