假设我在终端中运行这样的命令:
~$ echo 'sleep 2; echo "hello!"' | sh
然后开始输入下一行。两秒钟后,“hello!\n”一词将被插入到我正在写的内容中。我知道有一个解决方法(按向上然后向下刷新提示),但是在没有历史记录的其他系统上——例如,通过 telnet 使用 MUD——这是不可能的。
有人知道将 stdin 与 stdout 分开的 ncurses 应用程序或终端模拟器吗?这看起来在 ncurses 中很容易实现,你只需要使用一些聪明的 dup2s,但在我实现它之前,我想知道以前是否有人做过。
也欢迎针对主要问题的任何其他解决方案。
答案1
这并不像听起来那么容易改变。它与终端“cooked”与“raw”模式以及是否启用“echo”有关。
当终端处于烘焙模式(默认)时,内核读取作为输入输入的所有内容,并使用基本的行编辑功能对其进行处理,其中包括立即回显普通文本、处理擦除和删除字符(擦除单个字符和整个字符)分别是当前行的内容,以及其他一些内容。当您按 Enter 键时,文本行实际上才会出现在终端的输入中。在按下 Enter 键之前的整个过程中,一切都完全发生在内核内部,并且终端上运行的进程没有接收到单个字节,因此前台应用程序甚至不知道用户正在输入任何内容。在 tty 上运行的进程无法抑制此回显,即使它想要这样做,只是因为回显会在不合时宜的时间出现(例如与输出混合),因为这样的进程甚至不知道输入正在发生。
您可以将终端设置为原始模式,而不是不显示任何回显来抑制此情况(stty raw
或使用 termios),但随后您将完全失去内核的行编辑功能 - 这意味着您无法通过按Ctrl-u并重新开始来纠正拼写错误。更重要的是,使用任何依赖于内核熟处理的程序(基本上是任何不使用 readline 或 ncurses 的程序)时,您都会遇到很多麻烦,因为您将在此类程序中完全盲目地输入!哦,还有:如果没有终端熟处理,您将失去内核对中断和挂起的作业控制快捷方式的拦截(分别默认为Ctrl-c和Ctrl- z)。
答案2
您可以在后台包含一些 ANSI 转义,echo
以将其输出发送到屏幕顶部:
printf %b\\n 'sleep 2; printf "\0337\033[H\033[Khello!\0338"' |sh
应该可以做吧。它需要一个基本兼容 ANSI 的终端才能工作,但如果您还没有其中之一,那么您可能应该这样做。自 80 年代以来,其他人都在使用它们。
在每种情况下,前导\033
都是文字的八进制转义<ESC>字符 - 例如您可能按键盘的左上角。所有其余序列都由终端在输入时解释为光标寻址命令。
他们做这些事情:
7
- 保存光标状态以供以后恢复。
[H
- 将光标移至主页 - 将其移至屏幕第一行的第一列。
[K
- 擦除当前行上的文本。
8
- 恢复上次保存的光标状态。
结果是后台进程将光标定位到屏幕的左上角,清除该行,写出你好!,然后将光标放回到找到它的位置。更复杂的组合是可能的 - 并且可以用来开发更强大的解决方案,但这就是我得到的。
答案3
我知道的唯一解决方案是按^R
。它恢复输入的文本。这并不完美,但比坚持backspace
一段时间然后重新开始要好。
答案4
用于通过 telnet 玩游戏的用例。我曾经使用单独的终端进行输入。
通过执行以下操作创建输入终端:
xterm -e bash -c "cat > '$(readlink -f /dev/stdin)'"