如果在等待`read -s`时被中断,则在子进程中运行bash会破坏tty的stdout. [英] Running bash in subprocess breaks stdout of tty if interrupted while waiting on `read -s`?

查看:152
本文介绍了如果在等待`read -s`时被中断,则在子进程中运行bash会破坏tty的stdout.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

@Bakuriu在评论中指出,这基本上与 BASH:输入时按Ctrl + C会断开当前终端的操作,但是,只有当bash作为另一个可执行文件的子进程运行而不是直接从bash那里运行终端清理正常时,我才能重现该问题.对于为什么bash在这方面似乎被打破的答案,我会很感兴趣.

As @Bakuriu points out in the comments this is basically the same problem as in BASH: Ctrl+C during input breaks current terminal However, I can only reproduce the problem when bash is run as a subprocess of another executable, and not directly from bash, where it seems to handle terminal cleanup fine. I would be interested in an answer as to why bash seems to be broken in this regard.

我有一个Python脚本,用于记录由该脚本启动的子进程的输出.如果子进程碰巧是一个bash脚本,该脚本有时会通过调用内置的read -s(-s,这是防止输入的字符作为键)来读取用户输入的内容,并且用户中断了该脚本(即,按Ctrl-C),那么bash即使继续接受输入,也无法将输出恢复到tty.

I have a Python script meant to log the output of subprocess that is started by that script. If the subprocess happens to be a bash script that at some point reads user input by calling the read -s built-in (the -s, which prevents echoing of entered characters, being key), and the user interrupts the script (i.e. by Ctrl-C), then bash fails to restore output to the tty, even though it continues to accept input.

我将其简化为一个简单的示例:

I whittled this down to a simple example:

$ cat test.py
#!/usr/bin/python
import subprocess as sp
p = sp.Popen(['bash', '-c', 'read -s foo; echo $foo'])
p.wait()

在运行./test.py时,它将等待一些输入.如果键入一些输入并按Enter,则脚本将返回并按预期回显您的输入,并且没有问题.但是,如果立即单击"Ctrl-C",Python将显示KeyboardInterrupt的追溯,然后返回到bash提示符.但是,您键入的内容均不会显示在终端上.但是,键入reset<enter>会成功重置终端.

Upon running ./test.py it will wait for some input. If you type some input and press Enter the script returns and echos your input as expected, and there is no issue. However, if you immediately hit "Ctrl-C", Python displayed a traceback for the KeyboardInterrupt, and then returns to the bash prompt. However, nothing you type is displayed to the terminal. Typing reset<enter> successfully resets the terminal, however.

我对这里发生的事情一无所知.

I'm somewhat at a loss as to exactly what's happening here.

更新:我也设法在没有混合Python的情况下重现了这一点.我试图在strace中运行bash以查看是否可以收集正在发生的任何事情.使用以下bash脚本:

Update: I managed to reproduce this without Python in the mix either. I was trying to run bash in strace to see if I could glean anything that was going on. With the following bash script:

$ cat read.sh
#!/bin/bash
read -s foo
echo $foo

运行strace ./read.sh并立即按Ctrl-C会产生:

Running strace ./read.sh and immediately hitting Ctrl-C produces:

...
ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon -echo ...}) = 0
brk(0x1a93000)                          = 0x1a93000
read(0, Process 25487 detached
 <detached ...>

其中PID 25487是read.sh.这使终端处于相同的断开状态.但是,strace -I1 ./read.sh只是中断./read.sh进程并返回到正常的,不中断的终端.

Where PID 25487 was read.sh. This leaves the terminal in the same broken state. However, strace -I1 ./read.sh simply interrupts the ./read.sh process and returns to a normal, non-broken terminal.

推荐答案

似乎这与bash -c启动非交互式 shell的事实有关.这可能会阻止它恢复终端状态.

It seems like this is related to the fact that bash -c starts a non-interactive shell. This probably prevents it from restoring the terminal state.

要显式启动交互式shell,只需将-i选项传递给bash.

To explicitly start an interactive shell you can just pass the -i option to bash.

$ cat test_read.py 
#!/usr/bin/python3
from subprocess import Popen
p = Popen(['bash', '-c', 'read -s foo; echo $foo'])
p.wait()
$ diff test_read.py test_read_i.py 
3c3
< p = Popen(['bash', '-c', 'read -s foo; echo $foo'])
---
> p = Popen(['bash', '-ic', 'read -s foo; echo $foo'])

当我运行并按 Ctrl + C 时:

When I run and press Ctrl+C:

$ ./test_read.py

我获得:

Traceback (most recent call last):
  File "./test_read.py", line 4, in <module>
    p.wait()
  File "/usr/lib/python3.5/subprocess.py", line 1648, in wait
    (pid, sts) = self._try_wait(0)
  File "/usr/lib/python3.5/subprocess.py", line 1598, in _try_wait
    (pid, sts) = os.waitpid(self.pid, wait_flags)
KeyboardInterrupt

并且终端没有正确还原.

and the terminal isn't properly restored.

如果我以得到以下相同的方式运行test_read_i.py文件:

If I run the test_read_i.py file in the same way I just get:

$ ./test_read_i.py 

$ echo hi
hi

没有错误,终端可以正常工作.

no error, and terminal works.

这篇关于如果在等待`read -s`时被中断,则在子进程中运行bash会破坏tty的stdout.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆