繁体   English   中英

使用 Python subprocess.Popen 和 pty 在哑终端中运行交互式 Bash

[英]Run interactive Bash in dumb terminal using Python subprocess.Popen and pty

这个问题类似于使用 popen 和专用的 TTY Python 运行交互式 Bash ,除了我想在“哑”终端( TERM=dumb )中运行 Bash,并且不将 tty 置于原始模式。

下面的代码是我的尝试。 该代码类似于链接问题中给出的解决方案,主要区别在于它不会将 tty 置于原始模式,并设置TERM=dumb

import os
import pty
import select
import subprocess
import sys

master_fd, slave_fd = pty.openpty()

p = subprocess.Popen(['bash'],
                     stdin=slave_fd,
                     stdout=slave_fd,
                     stderr=slave_fd,
                     # Run in a new process group to enable bash's job control.
                     preexec_fn=os.setsid,
                     # Run bash in "dumb" terminal.
                     env=dict(os.environ, TERM='dumb'))

while p.poll() is None:
    r, w, e = select.select([sys.stdin, master_fd], [], [])
    if sys.stdin in r:
        user_input = os.read(sys.stdin.fileno(), 10240)
        os.write(master_fd, user_input)
    elif master_fd in r:
        output = os.read(master_fd, 10240)
        os.write(sys.stdout.fileno(), output)

上面的代码有两个问题:

  • 无论用户输入什么,代码都会重新回显。 例如,如果用户输入printf '' ,上面的代码将在打印下一个 bash 提示之前在下一行打印printf ''
  • Ctrl cCtrl d 的行为不像人们在bash所期望的那样。

我应该如何解决这些问题?

这正是不将 tty 置于原始模式的副作用。 通常,处理 pty 的程序(如 )会将外部 tty 置于原始模式。

  • 您的 Python 脚本的 tty(或 pty)会回显您输入的内容,并且新的 pty 会第二次回显。 您可以在新 pty 上禁用 ECHO。 例如:

     $ python3 using-pty.py bash-5.1$ echo hello echo hello hello bash-5.1$ stty -echo stty -echo bash-5.1$ echo hello # <-- no double echo any more hello bash-5.1$ exit exit
  • 您的 Python 脚本的 tty 未处于原始模式,因此当您按ctrl-d 时, Python 不会获得文字ctrl-d 相反,Python 会到达 EOF 并且read()返回一个空字符串。 因此,要使生成的外壳退出,您可以

     user_input = os.read(sys.stdin.fileno(), 10240) if not user_input: # explicitly send ctrl-d to the spawned process os.write(master_fd, b'\\04') else: os.write(master_fd, user_input)
  • 同样,Python 的 tty 不在原始模式下,所以当你按下ctrl-c 时,它不会得到文字ctrl-d 相反,它被杀死了。 作为一种解决方法,您可以捕获SIGINT

     def handle_sigint(signum, stack): global master_fd # send ctrl-c os.write(master_fd, b'\\03') signal.signal(signal.SIGINT, handle_sigint)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM