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

问题描述 投票:0回答:2

几周前我在这里问了一个与此相关的问题: Python、mpg123 和子进程未正确使用 stdin.write 或通信

感谢那里的帮助,我能够做我当时需要做的事情。 (没有调用 q,而是终止子进程来停止它)。## 标题 ## 但现在我似乎又陷入了另一点混乱。

    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 -Cwhatever.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')
python subprocess pty
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

中的大部分功能(不要创建任何管道,并记住您最终会执行

two
fork,因此请确保设置正确的父母/孩子关系)。

另请注意,您需要将路径传递给

mpg123

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

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

在 Windows 上,没有
pty

这样的东西,并且根本无法使用 Python 内置的工具来做到这一点。您将需要使用各种第三方库之一来控制 cmd.exe 控制台(也称为 DOS 提示符)。

    


1
投票

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')

© www.soinside.com 2019 - 2024. All rights reserved.