Run interactive Bash with popen and a dedicated TTY Python

后端 未结 2 915
眼角桃花
眼角桃花 2020-11-28 11:03

I need to run an interactive Bash instance in a separated process in Python with it\'s own dedicated TTY (I can\'t use pexpect). I used this code snippet I commonly see used

相关标签:
2条回答
  • 2020-11-28 11:59

    This is a solution to run an interactive command in subprocess. It uses pseudo-terminal to make stdout non-blocking(also some command needs a tty device, eg. bash). it uses select to handle input and ouput to the subprocess.

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import os
    import sys
    import select
    import termios
    import tty
    import pty
    from subprocess import Popen
    
    command = 'bash'
    # command = 'docker run -it --rm centos /bin/bash'.split()
    
    # save original tty setting then set it to raw mode
    old_tty = termios.tcgetattr(sys.stdin)
    tty.setraw(sys.stdin.fileno())
    
    # open pseudo-terminal to interact with subprocess
    master_fd, slave_fd = pty.openpty()
    
    # use os.setsid() make it run in a new process group, or bash job control will not be enabled
    p = Popen(command,
              preexec_fn=os.setsid,
              stdin=slave_fd,
              stdout=slave_fd,
              stderr=slave_fd,
              universal_newlines=True)
    
    while p.poll() is None:
        r, w, e = select.select([sys.stdin, master_fd], [], [])
        if sys.stdin in r:
            d = os.read(sys.stdin.fileno(), 10240)
            os.write(master_fd, d)
        elif master_fd in r:
            o = os.read(master_fd, 10240)
            if o:
                os.write(sys.stdout.fileno(), o)
    
    # restore tty settings back
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
    
    0 讨论(0)
  • 2020-11-28 12:05

    This is the solution that worked for me at the end (as suggested by qarma) :

    libc = ctypes.CDLL('libc.so.6')
    
    master, slave = pty.openpty()
    p = subprocess.Popen(["/bin/bash", "-i"], preexec_fn=libc.setsid, stdin=slave, stdout=slave, stderr=slave)
    os.close(slave)
    
    ... do stuff here ...
    
    x = os.read(master, 1026)
    print x
    
    0 讨论(0)
提交回复
热议问题