简体   繁体   中英

run command in interactive opend bash with python

I use this script to run interactive VLC bash:

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"

now in this bash opend I like to run 'info' command, How to do this?

if in bash I type

  vlc -I rc --novideo --noaudio --rc-fake-tty -q  udp://@1.2.3.4:1234

It shows this

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.
>

and It wait for get a command.

This can be done with pure pipes, but it's going to be hard. And even harder if you use os.popen() instead of using subprocess .

The right way to script an interactive program is to use a higher-level library that's designed to make it easy, like pexpect . Then you just write something like:

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

However, a much better solution is to not run VLC in interactive mode; just run it in batch mode and pass it commands. Going out of your way to have it treat your input as a TTY just so you can try to figure out how to act like a human at a TTY is making things harder for no good reason.

Or, even better, use libVLC instead. As you can see from that link, there are Python bindings for it.


If you really want to do it interactively, and you want to do it manually over pipes, you will have to be very careful. If you don't mind just deadlocking on any unexpected results, you can do something like this:

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.

As you can see, this is a lot less fun.


And if you insist on using os.open instead even though it's deprecated and even more painful to use, you obviously can't write to it if you open the pipe in the default 'r' mode, just like any other file-like object, and of course tacking .read() on the end means you don't even have the popen object anymore, you just stored the first buffer it gave you and then leaked the handle. If you change that to open in 'r+' mode, if that works on your platform, and you store the popen object itself, you can use it similarly to the subprocess.Popen object above, using child.write and child.read instead of child.stdin.write and child.stdout.read`.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM