payton - python powered
Ejecute Bash interactivo con Popen y un TTY TTY dedicado (3)
tendrá que pasar la entrada para leer y luego imprimir x.
Necesito ejecutar una instancia interactiva de Bash en un proceso separado en Python con su propio TTY dedicado (no puedo usar pexpect). Utilicé este fragmento de código que comúnmente veo utilizado en programas similares:
master, slave = pty.openpty()
p = subprocess.Popen(["/bin/bash", "-i"], stdin=slave, stdout=slave, stderr=slave)
os.close(slave)
x = os.read(master, 1026)
print x
subprocess.Popen.kill(p)
os.close(master)
Pero cuando lo ejecuto obtengo el siguiente resultado:
$ ./pty_try.py
bash: cannot set terminal process group (10790): Inappropriate ioctl for device
bash: no job control in this shell
Strace of the run muestra algunos errores:
...
readlink("/usr/bin/python2.7", 0x7ffc8db02510, 4096) = -1 EINVAL (Invalid argument)
...
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffc8db03590) = -1 ENOTTY (Inappropriate ioctl for device)
...
readlink("./pty_try.py", 0x7ffc8db00610, 4096) = -1 EINVAL (Invalid argument)
El fragmento de código parece bastante sencillo, ¿Bash no está recibiendo algo que necesita? ¿Cual podría ser el problema aquí?
Esta es la solución que funcionó para mí al final (como lo sugirió 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
Esta es una solución para ejecutar un comando interactivo en subproceso. Utiliza pseudo-terminal para hacer que stdout no bloquee (también un comando necesita un dispositivo tty, por ejemplo, bash). usa seleccionar para manejar la entrada y salida al subproceso.
#!/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)