几个星期前我在这里问了一个与此有关的问题: 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')
答案 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')