我知道有很多类似的问题,例如this one或this one,可能还有一些,但它们似乎都不适用于我的特殊情况。我对subprocess.Popen()
的工作方式缺乏了解也无济于事。
我想要实现的是:启动一个子进程(命令行无线电播放器),它也将数据输出到终端,也可以接收输入 - 等待一段时间 - 终止子进程 - 退出shell。我在OSX 10.9上运行python 2.7
案例1。 这将启动无线电播放器(但仅限音频!),终止该过程,退出。
import subprocess
import time
p = subprocess.Popen(['/bin/bash', '-c', 'mplayer http://173.239.76.147:8090'],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False,
stderr=subprocess.STDOUT)
time.sleep(5)
p.kill()
案例2。 这将启动无线电播放器,输出无线电名称,歌曲,比特率等信息,并接受输入。它终止子进程,但它永远不存在shell,即使在使用' Ctrl-C'终端也无法使用。
p = subprocess.Popen(['/bin/bash', '-c', 'mplayer http://173.239.76.147:8090'],
shell=False)
time.sleep(5)
p.kill()
关于如何做的任何想法?如果没有其他选择的话,我甚至在考虑为子流程打开 slave-shell 的可能性(当然它也是我不知道的东西)。谢谢!
答案 0 :(得分:3)
似乎mplayer
使用curses
库,kill()
或terminate()
时,由于某种原因,它不会清理库正确地陈述。
要恢复终端状态,您可以使用reset
命令。
演示:
import subprocess, time
p = subprocess.Popen(['mplayer', 'http://173.239.76.147:8090'])
time.sleep(5)
p.terminate()
p.wait() # important!
subprocess.Popen(['reset']).wait()
print('Hello, World!')
原则上应该可以使用stty sane
,但它对我来说效果不佳。
正如塞巴斯蒂安指出的那样,上面的代码(现已添加)中缺少wait()
次调用。通过此wait()
使用terminate()
调用和,终端不会搞砸(因此不需要reset
)。
没有wait()
我有时会遇到python进程和mplayer
之间混合输出的问题。
另外,正如Sebastian所指出的,mplayer
特有的解决方案是将q
发送到mplayer
的标准输出以退出它。
我保留使用reset
的代码,因为它适用于使用curses
库的任何程序,无论它是否正确地删除了库,因此它在其他干净退出不可能的情况下可能会有用。
答案 1 :(得分:2)
我想要实现的是:启动一个子进程(命令行无线电播放器),它也将数据输出到终端,也可以接收输入 - 等待一段时间 - 终止子进程 - 退出shell。我在OSX 10.9上运行python 2.7
在我的系统上,mplayer
接受键盘命令,例如,q
停止播放并退出:
#!/usr/bin/env python
import shlex
import time
from subprocess import Popen, PIPE
cmd = shlex.split("mplayer http://www.swissradio.ch/streams/6034.m3u")
p = Popen(cmd, stdin=PIPE)
time.sleep(5)
p.communicate(b'q')
它开始mplayer
调整为公共领域经典;等了5秒;要求mplayer
退出并等待它退出。输出将转到终端(与python脚本输出相同的位置)。
我还尝试了p.kill()
,p.terminate()
,p.send_signal(signal.SIGINT)
( Ctrl + C )。 p.kill()
创建了该进程挂起的展示。可能的解释:p.kill()
会打开一些管道,例如,如果stdout=PIPE
那么您的Python脚本可能会挂起p.stdout.read()
,即它会杀死父mplayer
进程,但可能会有一个孩子保持管道打开的过程。 p.terminate()
,p.send_signal(signal.SIGINT)
- mplayer
以有序的方式退出。我尝试过的所有变种都不需要reset
。
我应该如何同时使用Python和键盘输入?我是否需要两个不同的子流程以及如何将键盘输入重定向到PIPE?
放弃stdin=PIPE
并拨打p.terminate(); p.wait()
而不是p.communicate(b'q')
会更简单。
如果你想保留stdin=PIPE
,那么一般原则是:从sys.stdin
读取,写入p.stdin
直到超时发生。鉴于mplayer
需要一个字母命令,您需要能够read one character at at time from sys.stdin
。写入部分很简单:p.stdin.write(c)
(设置bufsize=0
以避免在Python端缓冲。mplayer
不会缓冲其标准输入,因此您不必担心它)。
您不需要两个不同的子流程。要实施timeout
,您可以在threading.Timer(5, p.stdin.write, [b'q']).start()
上使用select.select
或sys.stdin
超时。
我想使用旧的raw_input的东西与它无关,或者?
raw_input()
不适合mplayer
,因为它会读取整行,但mplayer
一次需要一个字符。