Python pty.spawn stdin is not echoed, but redirected to master stdout

I want to call a program from Python and make it believe that it stdoutis tty, even when the stdout of the Python process is bound to a pipe. Therefore, I used a function pty.spawnto achieve this, which can be verified from the following:

$ python -c "import sys; from subprocess import call; call(sys.argv[1:])" python -c "import sys; print sys.stdout.isatty()" | cat
False

$ python -c "import sys; import pty; pty.spawn(sys.argv[1:])" python -c "import sys; print sys.stdout.isatty()" | cat
True

(We see that in the second command we achieved our goal, i.e. the generated process is tricked into thinking that its stdout is tty.)

But the problem is that if we use pty.spawn, then its input will not be an echo, rather, it is redirected to master stdout. This can be seen by the following command:

$ python -c "import sys; import pty; pty.spawn(sys.argv[1:])" cat > out.txt
$ # Typed "hello" in input, but that is not echoed (use ^D to exit). It is redirected to output.txt
$ cat out.txt
hello
hello

(But this problem does not exist when we use subprocess.call

$ python -c "import sys; from subprocess import call; call(sys.argv[1:])" cat > out1.txt
hello
$ cat out1.txt
hello

since its stdin and stdout are correctly bound to the master.)

, Python, stdout tty ( pty.spawn), ( subprocess.call). ?

+6
1

stdout, , -, , , .

, spawn : pty pty.fork() stdin/stdout. .

spawn, :

1: , , , ( here python)

$ python <(cat << EOF
import sys
import pty
print 'start to stdout only'
pty.spawn(sys.argv[1:])
print 'complete to stdout only'
EOF
) bash -c 'cat > out.txt'

:

start to stdout only
hello
complete to stdout only

, ( ) . out.txt :

$ cat out.txt
hello

, .

2:. , , , out python , - , :

python <(cat << EOF
import sys
import pty
import os
old_stdout = sys.stdout
sys.stdout = myfdout = os.fdopen(4,"w")
print 'start to out file only'
myfdout.flush()
pty.spawn(sys.argv[1:])
print 'complete to out file only'
sys.stdout = old_stdout
EOF
) bash -c 'cat >&4' 4>out.txt

( , ):

hello

out :

$ cat out.txt
start to out file only
hello
complete to out file only

: python pty : , python- stdout stdin. Id , pty.fork(), stdin/stdout .

stdout python . , pty , - stdin stdout . stdout () , pty.

1 - stdout - pty.spawn, pty stdout ( stdin )

2 (, 4) stdout, python (.. )

pty, pty.spawn - , stdout . - stdin.

, pty , , pty.fork() ( , , pty.spawn)

EDIT pty.fork():

import sys
import pty
import os
import select
import time
import tty
import termios

print 'start'
try:
    pid, fd = pty.fork()
    print 'forked'
except OSError as e:
    print e

if pid == pty.CHILD:
    cmd = sys.argv[1]
    args = sys.argv[1:]
    print cmd, args
    os.execvp(cmd,args)
else:
    tty.setraw(fd, termios.TCSANOW)
    try:
        child_file = os.fdopen(fd,'rw')
        read_list = [sys.stdin, child_file]
        while read_list:
            ready = select.select(read_list, [], [], 0.1)[0]
            if not ready and len(read_list) < 2:
                break
            elif not ready:
                time.sleep(1)
            else:
                for file in ready:
                    try:
                        line = file.readline()
                    except IOError as e:
                        print "Ignoring: ", e
                        line = None
                    if not line:
                        read_list.remove(file)
                    else:
                        if file == sys.stdin:
                            os.write(fd,line)
                        else:
                            print "from child:", line
    except KeyboardInterrupt:
        pass

EDIT question pty.fork()

: pty.fork():

pty.fork(), : , , , pty.fork().

- , (), - ().

pid fd -, stdin stdout: , fd, , ; fd, childs stdin. , stdout/stdin.

pid 0, fd . , stdin/stdout, , - .

, , pid. , , , - . :

if pid == pty.CHILD:
  #child thread will execute this code
  ....
else
  #parent thread will execute this code
  ...

pty. os.execvp , pty , , pty.spawn()'. This means the child stdin/stdout are now connected to the command you wanted via a pty. IMmportantly, any input or output from the command (or the pty for that matter) will be available to the parent thread by reading from fd . And the parent can write to the command via pty by writing to fd`

, , , stdin/stdout stdin/stdout fd. , ( else). , stdin, fd. , fd (), . , , , - stdin/stdout fd. - , , .

, , :

tty.setraw(fd, termios.TCSANOW)

pty -.

, : - - - (.. ) - - , stdout, stdin/stdout - - stdin

- - ?

+3

Source: https://habr.com/ru/post/1016405/


All Articles