views:

206

answers:

2

Hi I'm writing a psudo-terminal that can live in a tty and spawn a second tty which is filters input and output from

I'm writing it in python for now, spawning the second tty and reading and writing is easy

but when I read, the read does not end, it waits for more input.

import subprocess

pfd = subprocess.Popen(['/bin/sh'], shell=True, 
    stdout=subprocess.PIPE, stdin=subprocess.PIPE)

cmd = "ls" 

pfd.stdin.write(cmd + '\n')

out = ''
while 1: 
    c = pfd.stdout.read(1)
    if not c: # if end of output (this never happends)
        break 
    if c == '\n': # print line when found
        print repr(out)
        out = ''
    else:
        out += c

----------------------------- outputs ------------------------

intty $ python intty.py 
'intty.py'
'testA_blank'
'testB_blank'
(hangs here does not return)

it looks like it's reaching the end of hte buffer and instead of returning None or '' it hangs waiting for more input.

what should I be looking for to see if the output has completed? the end of the buffer? a non-printable character?

---------------- edit -------------

this happends also when I run xpcshell instead of ls, I'm assuming these interactive programs have some way of knowing to display the prompt again, strangly the prompt, in this case "js>" never apears

A: 

For applications like a shell, the output will not end until the shell ends. Either use select.select() to check if it has more output waiting for you, or end the process.

Ignacio Vazquez-Abrams
+1  A: 

Well, your output actually hasn't completed. Because you spawned /bin/sh, the shell is still running after "ls" completes. There is no EOF indicator, because it's still running.

Why not simply run /bin/ls?

You could do something like

pfd = subprocess.Popen(['ls'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)

out, err_output = pfd.communicate()

This also highlights subprocess.communicate, which is a safer way to get output (For outputs which fit in memory, anyway) from a single program run. This will return only when the program has finished running.

Alternately, you -could- read linewise from the shell, but you'd be looking for a special shell sequence like the sh~# line which could easily show up in program output. Thus, running a shell is probably a bad idea all around.


Edit Here is what I was referring to, but it's still not really the best solution, as it has a LOT of caveats:

while 1: 
    c = pfd.stdout.read(1)
    if not c:
        break
    elif c == '\n': # print line when found
        print repr(out)
        out = ''
    else:
        out += c
        if out.strip() == 'sh#':
            break

Note that this will break out if any other command outputs 'sh#' at the beginning of the line, and also if for some reason the output is different from expected, you will enter the same blocking situation as before. This is why it's a very sub-optimal situation for a shell.

Crast
the idea is that I can run another command when the first is done
Fire Crow
What's wrong with just running the next command in another Popen, then? It's not like the shell is doing anything much different than `popen(3)` or `fork(2)` followed by `exec(3)` to run subcommands, which is exactly what `subprocess.Popen` does. In addition, running the program directly gives you better access to its file descriptors, as well as makes it easier to check the return code.
Crast
it is wrong for what I need, I'm designing it for interactive processes, such as xpcshell, lisp implimentations cheap ftp clients etc.
Fire Crow
"Alternately, you -could- read linewise from the shell, but you'd be looking for a special shell sequence like the sh~#" this may be what I need
Fire Crow
Well, if you do this (using something like `readline()` on the pipe, you could -still- enter a blocking situation like you had in your example, depending on the program you are running (if you are trying to make this generic) You will probably need to keep reading character-wise (like you were doing) until you see some sort of output that matches with something. Just realize there's no "magic bullet" in most of these protocols, and blocking situations may occur. In which case, like @Ignacio said, `select()` (or a library based on async io) may be the right answer.
Crast
`s/subprocess.communicate/pfd.communicate/`
J.F. Sebastian
@JF Sebastian, Thanks, will fix the example
Crast