簡體   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