当vim
通过调用时find | xargs
,如下所示:
find . -name "*.txt" | xargs vim
你会收到关于
Input is not from a terminal
之后终端的行为就完全不正常了。为什么就是它?
这个问题明确地是关于为什么,而不是关于如何避免有人问过这个问题,也有人回答过这个问题,别处。
答案1
当您通过 调用程序时xargs
,程序的 stdin(标准输入)指向/dev/null
。(因为 xargs 不知道原来的stdin,它是第二好的选择。)
$ true | xargs菲兰-s 0 chrdev /dev/null 1 tty /dev/pts/1 2 tty /dev/pts/1 $ true | xargs ls -l /dev/fd/
Vim 期望其 stdin 与其控制终端相同,并执行各种与终端相关的读写控制直接在 stdin 上执行。在/dev/null
(或任何非 tty 文件描述符)上执行时,这些 ioctl 毫无意义并返回 ENOTTY,它会被默默忽略。
我猜测更具体的原因:启动时 Vim 会读取并记住旧的终端设置,并在退出时恢复它们。在我们的例子中,当为非 tty fd(文件描述符)请求“旧设置”时,Vim 收到的所有值都是空的,所有选项都是禁用的,并草率地将相同的设置设置到您的终端。
你可以通过运行 来看到这一点
vim < /dev/null
,退出它,然后运行stty
,这将输出一大堆<undef>
s。在 Linux 上,运行stty sane
将使终端再次可用(尽管它将要失去了这样的选择iutf8
,可能会在以后造成一些小烦恼)。
你可以认为这是 Vim 的一个 bug,因为它能打开/dev/tty
终端控制,但实际上没有。(在启动过程中的某个时刻,Vim 会将其 stderr 复制到 stdin,这允许它读取您的输入命令 – 从打开用于写入的 fd – 但即使这样也做得不够早。)
答案2
(根据 grawity 的解释,这指向xargs
。)stdin
/dev/null
这解决方案解决这个问题的方法是将-o
参数添加到xargs
。从man xargs
:
-o
/dev/tty
在执行命令之前, 像在子进程中一样重新打开 stdin 。如果你想xargs
运行交互式应用程序,这很有用。
因此,以下代码行应该适合您:
查找 . -name "*.txt" | xargs -o vim
GNU xargs 自 2017 年的某个版本起就支持此扩展(具有长选项名称--open-tty
)。
对于较旧或其他版本的 xargs,可以明确传入/dev/tty
以解决问题:
find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme
(ignoreme
这里占用了 $0,因此 $@ 是来自 xargs 的所有参数。)
答案3
最简单的方法:
vim $(find . -name "*foo*")
答案4
改用 GNU Parallel:
find . -name "*.txt" | parallel -j1 --tty vim
或者如果您想一次性打开所有文件:
find . -name "*.txt" | parallel -Xj1 --tty vim
它甚至可以正确处理如下文件名:
My brother's 12" records.txt
观看介绍视频以了解更多信息:http://www.youtube.com/watch?v=OpaiGYxkSuQ