Vim: Warning: input is not from a terminal 后退出 vim 终端异常
今天执行了如下命令调用 vi 来打开 find 搜索到的文件:
longyu@longyu-pc:~$ find ./ -name 'linux-kernel-ixgbe-commit-log' | xargs vi
执行了上述命令之后,Vim 打印了下面的警告信息:
Vim: Warning: Input is not from a terminal
没理会这个警告,退出 vim 后,发现终端异常,输入的字符不能正常显示,按 enter 换行也不正确。
具体的现象如下:
longyu@longyu-pc:~$ longyu@longyu-pc:~$ longyu@longyu-pc:~$ longyu@longyu-pc:~$
问题分析
出了问题之后,之前提到的那个警告需要重视一下。在网上搜索,发现了如下链接:
invoking vi through find xargs breaks my terminal Why
通过阅读上述链接上的问题,我发现这个问题是 xargs 将 stdin 映射为 /dev/null 导致的。按照上述链接中的回答,我在终端中执行如下命令:
true | xargs filan -s
执行后报 filan 命令找不到的错误。直接 sudo apt-get install filan
没有找到相关包,搜索发现 filan 是在 socat 包中包含的命令。执行 sudo apt-get install socat
命令之后,filan 命令可以使用了。
filan -h
了解到 filan 命令是用来分析进程的文件描述符信息的程序。再次执行 true | xargs filan -s
命令,有如下输出信息:
longyu@longyu-pc:~$ true | xargs filan -s
0 chrdev /dev/null
1 tty /dev/pts/3
2 tty /dev/pts/3
3 unixdatagram <anon>
4 unixdatagram <anon>
从上面的输出中可以确定 xargs 程序将 stdin 映射到了 /dev/null 上。进一步的了解发现 xargs 支持 -o 选项,可以在执行子进程命令之前重新将 stdin 映射到 /dev/tty 上,这个功能可以用在使用 xargs 调用交互式程序的情况下。
man xargs 中相关的内容如下:
-o, --open-tty
Reopen stdin as /dev/tty in the child process before executing the command. This is useful if you want xargs to run an interactive appli‐
cation.
指定 -o 参数再次执行 filan -s 命令,有如下输出:
longyu@longyu-pc:~$ true | xargs -o filan -s
0 tty /dev/tty
1 tty /dev/pts/3
2 tty /dev/pts/3
3 unixdatagram <anon>
4 unixdatagram <anon>
从上面的输出中可以确定,stdin 被映射到了 /dev/tty 上。
如何将终端恢复正常
最开始,我直接关闭终端,然后重新开了一个新的终端就解决了问题。其实我可以通过执行 stty sane
命令来将终端恢复正常。(注意输入命令并不回显)
man stty 发现 sane 选项会将所有特殊字符恢复默认值,这与正常工作过程中终端的特殊字符配置可能不相同。在我系统中,正常的终端特殊字符配置如下:
longyu@longyu-pc:~$ stty
speed 38400 baud; line = 0;
-brkint -imaxbel
直接使用 xargs 调用一次 vi 然后通过执行 stty sane 来恢复默认终端配置,这之后的特殊字符配置信息如下:
longyu@longyu-pc:~$ stty
speed 0 baud; line = 0;
通过对比可以发现,调用 stty sane 之后,一些终端配置失效了。
最终的解决方案
不要通过 xargs 直接调用交互式程序。非要通过 xargs 来调用交互式程序的话,指定 xargs 的 -o 参数在调用交互式命令执行前将 stdin 重新映射为 /dev/tty。