在Python中以交互模式运行命令的bash

0 投票
1 回答
1988 浏览
提问于 2025-04-18 15:48

我用这个脚本来运行交互式的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.writechild.read,而不是 child.stdin.writechild.stdout.read

撰写回答