tylerl
tylerl

Reputation: 30847

Indicate no more input without closing pty

When controlling a process using a PTY master/slave pair, I would like to indicate to the process in question that stdin has closed and I have no more content to send, but I would still like to receive output from the process.

The catch is that I only have one file descriptor (the PTY "master") which handles both input from the child process and output to the child process. So closing the descriptor would close both.

Example in python:

import subprocess, pty, os
master,slave = pty.openpty()
proc = subprocess.Popen(["/bin/cat"], stdin=slave, stdout=slave)
os.close(slave)  # now belongs to child process
os.write(master,"foo")
magic_close_fn(master)  # <--- THIS is what I want
while True:
    out = os.read(master,4096)
    if out: 
        print out
    else: 
        break
proc.wait()

Upvotes: 3

Views: 1070

Answers (3)

a1291762
a1291762

Reputation: 336

I had to do this today, ended up here and was sad to see no answer. I achieved this using a pair of ptys rather than a single pty.

stdin_master, stdin_slave = os.openpty()
stdout_master, stdout_slave = os.openpty()
def child_setup():
    os.close(stdin_master) # only the parent needs this
    os.close(stdout_master) # only the parent needs this

with subprocess.Popen(cmd,
        start_new_session=True,
        stderr=subprocess.PIPE,
        stdin=stdin_slave,
        stdout=stdout_slave,
        preexec_fn=child_setup) as proc:
    os.close(stdin_slave) # only the child needs this
    os.close(stdout_slave) # only the child needs this
    stdin_pty = io.FileIO(stdin_master, "w")
    stdout_pty = io.FileIO(stdout_master, "r")

    stdin_pty.write(b"here is your input\r")
    stdin_pty.close() # no more input (EOF)
    output = b""
    while True:
        try:
            output += stdout_pty.read(1)
        except OSError:
            # EOF
            break
    stdout_pty.close()

Upvotes: 1

jyz
jyz

Reputation: 6199

I think that what you want is to send the CTRL-D (EOT - End Of Transmission) caracter, isn't you? This will close the input in some applications, but others will quit.

perl -e 'print qq,\cD,'

or purely shell:

echo -e '\x04' | nc localhost 8080

Both are just examples. BTW the CTRL-D caracter is \x04 in hexa.

Upvotes: 0

Linuxios
Linuxios

Reputation: 35783

You need to get separate read and write file descriptors. The simple way to do that is with a pipe and a PTY. So now your code would look like this:

import subprocess, pty, os
master, slave = pty.openpty()
child_stdin, parent_stdin = os.pipe()
proc = subprocess.Popen(["/bin/cat"], stdin=child_stdin, stdout=slave)
os.close(child_stdin)  # now belongs to child process
os.close(slave)
os.write(parent_stdin,"foo") #Write to the write end (our end) of the child's stdin
#Here's the "magic" close function
os.close(parent_stdin)
while True:
    out = os.read(master,4096)
    if out: 
        print out
    else: 
        break
proc.wait()

Upvotes: 0

Related Questions