Wrapping A Shell

Nick Craig-Wood nick at craig-wood.com
Wed Nov 29 07:30:03 EST 2006


Jeremy Moles <jeremy at emperorlinux.com> wrote:
>  I'm not sure if this is really the right place to ask this question, but
>  since the implementation is in Python, I figured I'd give it a shot.
> 
>  I want to "wrap" a shell process using popen inside of python program
>  rather than creating a new shell process for each line I process in the
>  app. For example, the code might look like:
> 
> 
>  	std = stdin, stdout, stderr = os.popen3("bash")
> 
>  	print >> stdin, "ls"
> 
>  	print stdout.readline()
> 
>  However, it appears my understanding of popen (or perhaps buffered IO)
>  is off somewhere, because this certainly doesn't work anything like I
>  expect it to (it hangs on stdout.readline).
> 
>  Obviously the example above is very contrived, but eventually I'll be
>  using this in an OpenGL "terminal" widget. Am I approaching this the
>  wrong way?

Short answer: use pexpect

  http://pexpect.sourceforge.net/

Long answer:

Firstly modern pythonistas use subprocess instead of os.popen*.

Secondly, buffering is going to cause you trouble and possible
deadlocks.

There are two ways to rid yourself of deadlock.

1) read and write from seperate threads / processes.  This is the
traditional unix way

2) use non blocking IO

Here is a possible way to solve the problems with subprocess (unix
only) and non blocking IO.

However I suspect you really want to use pexpect for this job...

from os import O_NONBLOCK
from errno import EAGAIN
from subprocess import Popen, PIPE
from fcntl import fcntl, F_SETFL, F_GETFL
from time import time

p = Popen(["/bin/bash", "-i"], shell=False, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)

# Make output streams non blocking (unix only)
for fd in (p.stdout.fileno(), p.stderr.fileno()):
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)

def read_output(p, timeout=1.0):
    """Read the output from the subprocess, with timeout"""
    end_time = time() + timeout
    output = ""
    while time() < end_time:
        try:
            output += p.stdout.read(1024)
        except IOError, e:
            if e.errno != EAGAIN:
                raise
    return output

p.stdin.write("ls\n")
print read_output(p)
p.stdin.write("uname -a\n")
print read_output(p)

-- 
Nick Craig-Wood <nick at craig-wood.com> -- http://www.craig-wood.com/nick



More information about the Python-list mailing list