通过 find | xargs 调用 vi 会破坏我的终端。为什么?

通过 find | xargs 调用 vi 会破坏我的终端。为什么?

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

相关内容