简体   繁体   English

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

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

This question is similar to Run interactive Bash with popen and a dedicated TTY Python , except that I want to run Bash in a "dumb" terminal ( TERM=dumb ), and without putting the tty into raw mode.这个问题类似于使用 popen 和专用的 TTY Python 运行交互式 Bash ,除了我想在“哑”终端( TERM=dumb )中运行 Bash,并且不将 tty 置于原始模式。

The code below is my attempt.下面的代码是我的尝试。 The code is similar to the solution given in the linked question, with the major difference that it does not put the tty into raw mode, and sets TERM=dumb .该代码类似于链接问题中给出的解决方案,主要区别在于它不会将 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)

There are two problems with the code above:上面的代码有两个问题:

  • The code will re-echo whatever the user inputs.无论用户输入什么,代码都会重新回显。 For example, if the user inputs printf '' , the code above will print printf '' on the next line before printing the next bash prompt.例如,如果用户输入printf '' ,上面的代码将在打印下一个 bash 提示之前在下一行打印printf ''
  • Ctrl c and Ctrl d do not behave as one would expect in bash . Ctrl cCtrl d 的行为不像人们在bash所期望的那样。

How should I fix these problems?我应该如何解决这些问题?

That's exactly the side effects of not putting the tty in raw mode.这正是不将 tty 置于原始模式的副作用。 Usually a program (like ) which handles pty would put the outer tty in raw mode.通常,处理 pty 的程序(如 )会将外部 tty 置于原始模式。

  • Your Python script's tty (or pty) echos what you input and the new pty echos for the 2nd time.您的 Python 脚本的 tty(或 pty)会回显您输入的内容,并且新的 pty 会第二次回显。 You can disable ECHO on the new pty.您可以在新 pty 上禁用 ECHO。 For example:例如:

     $ 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
  • Your Python script's tty is not in raw mode so when you press ctrl-d Python would not get the literal ctrl-d .您的 Python 脚本的 tty 未处于原始模式,因此当您按ctrl-d 时, Python 不会获得文字ctrl-d Instead, Python would reach EOF and read() returns an empty string.相反,Python 会到达 EOF 并且read()返回一个空字符串。 So to make the spawned shell exit you can因此,要使生成的外壳退出,您可以

     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)
  • Similarly, the Python's tty is not in raw mode so when you press ctrl-c , it'll not get the literal ctrl-d .同样,Python 的 tty 不在原始模式下,所以当你按下ctrl-c 时,它不会得到文字ctrl-d Instead it's killed.相反,它被杀死了。 As a workaround you can catch SIGINT .作为一种解决方法,您可以捕获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