几周前我在这里问了一个与此相关的问题: 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')
如果您确实需要控件,则不能只使用
Popen
。
mpg123
仅当其标准输入是 tty 时启用终端控制,而不是文件或管道。这就是为什么你会在横幅中看到这一行:
Terminal control enabled, press 'h' for listing of keys and functions.
Popen
(以及subprocess
,以及它所构建的 POSIX API)的全部要点就是管道。
那么,你能做什么呢?
pty
模块。它也可能在其他 *nix 平台上工作,但也可能不会 - 即使它被构建并包含在您的 stdlib 中。正如文档所说:
由于伪终端处理高度依赖于平台,因此只有适用于 Linux 的代码才能执行此操作。 (Linux 代码应该可以在其他平台上运行,但尚未经过测试。)
它肯定可以在 2.7 和 3.3 上的 *BSD 平台上运行,并且文档中的示例似乎可以在 Mac OS X 和 FreeBSD 上运行……但据我检查,这就是事实。
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 提示符)。
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')