在Python中以交互模式运行命令的bash
我用这个脚本来运行交互式的VLC命令行:
import os
import sys
if len(sys.argv) > 1:
tmp = os.popen('vlc -I rc --novideo --noaudio --rc-fake-tty -q udp://@1.2.3.4:1234').read()
else :
print "Error: no input"
现在在这个打开的命令行里,我想运行'info'这个命令,我该怎么做呢?
如果我在命令行里输入
vlc -I rc --novideo --noaudio --rc-fake-tty -q udp://@1.2.3.4:1234
它会显示这个
VLC media player 2.0.8 Twoflower (revision 2.0.8a-0-g68cf50b)
VLC media player 2.0.8 Twoflower
Command Line Interface initialized. Type `help' for help.
>
然后它会等待我输入一个命令。
1 个回答
2
这件事用纯管道是可以做到的,但会很麻烦。如果你用 os.popen()
而不是 subprocess
,那就更难了。
正确的做法是使用一个更高级的库,这种库是专门为简化这类操作而设计的,比如 pexpect
。这样你只需要写类似下面的代码:
import pexpect
child = pexpect.spawn('vlc -I rc --novideo --noaudio --rc-fake-tty -q udp://@1.2.3.4:1234')
child.expect('>')
child.sendline('info')
response = child.before
不过,更好的解决方案是不要在交互模式下运行 VLC;直接用批处理模式运行它,然后给它发送命令。为了让它把你的输入当成一个终端(TTY)来处理而特意去做这些,反而让事情变得更复杂,没必要。
或者,更好的选择是使用 libVLC。从那个链接可以看到,它有 Python 的绑定库。
如果你真的想以交互方式来做,并且想通过管道手动操作,你需要非常小心。如果你不介意在遇到意外结果时就死锁,你可以这样做:
import subprocess
child = subprocess.Popen(['vlc', '-I', 'rc', '--novideo', '--noaudio',
'--rc-fake-tty', '-q', 'udp://@1.2.3.4:1234'],
stdin=PIPE, stdout=PIPE)
def split_on_prompts():
rbuf = ''
while True:
newbuf = child.stdout.read()
rbuf += newbuf
out, prompt, rest = rbuf.partition('\n>')
if prompt:
yield out
rbuf = rest
if not newbuf:
yield rest
return
output = split_on_prompts()
banner = next(output)
child.stdin.write('info\n')
response = next(output)
# etc.
如你所见,这样做就没那么有趣了。
如果你坚持要用 os.open
,尽管它已经不推荐使用而且更麻烦,你显然不能在默认的 'r'
模式下写入,因为这就像其他文件对象一样。而且,如果你在后面加上 .read()
,那你就失去了 popen
对象,只存储了它给你的第一个缓冲区,然后就泄露了句柄。如果你把它改成 'r+'
模式(如果你的平台支持的话),并且存储 popen
对象本身,你就可以像上面提到的 subprocess.Popen
对象一样使用它,使用 child.write
和 child.read
,而不是 child.stdin.write
和 child.stdout.read
。