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


139

通过调用vimfind | xargs,如下所示:

find . -name "*.txt" | xargs vim

您收到有关的警告

Input is not from a terminal

然后终端的行为几乎崩溃。这是为什么?


11
旁注:您可以完全在vim内执行此操作,而无需使用find或完全不执行xargs。打开不带参数的vim,然后:args **/*.txt<CR>从编辑器内部运行以设置vim的参数。
Trevor Powell

3
@TrevorPowell:这些年来,vim从未停止过让我惊奇。
DevSolar 2013年



Answers:


101

通过调用程序时xargs,程序的stdin(标准输入)指向/dev/null。(由于xargs不知道原始的 stdin,所以它做的第二件事就是。)

$ true | xargs filan -s
    0 chrdev / dev /空
    1 tty / dev / pts / 1
    2 tty / dev / pts / 1

$ true | xargs ls -l / dev / fd /

Vim期望其stdin与它的控制终端相同,并直接在stdin上执行各种与终端相关的ioctl。完成处理/dev/null(或任何非tty文件描述符)后,这些ioctl将变得毫无意义,并返回ENOTTY,该提示将被静默忽略。

  • 我的一个更具体的原因是猜测:启动时,Vim会读取并记住旧的终端设置,并在退出时将其恢复。在我们的情况下,当为非tty fd(文件描述符)请求“旧设置”时,Vim会收到所有空值,并且禁用所有选项,并且不小心将其设置为您的终端。

    您可以通过运行vim < /dev/null,退出它然后运行来查看此内容stty,这将输出很多<undef>s。在Linux上,运行stty sane将再次使终端中使用(虽然它失去这样的选择iutf8,可能导致小烦恼更高版本)。

您可以将其视为Vim中的错误,因为它可以打开/dev/tty以进行终端控制,但不能打开。(在启动过程中的某个时候,Vim将其stderr复制到stdin,这使它可以从为写入而打开的fd中读取输入命令,但即使这样做还不够早,也是如此。)


20
+1,对于TL; DR人员只运行stty sane
doc_id

@rahmanisback:其他答案,加上Trevor的评论,都提供了避免终端损坏的方法。我接受了grawity的回答,因为我的问题是“为什么”,而不是“如何避免”,而这个问题实际上催生了这个问题
DevSolar 2015年

@DevSolar理解了,但是想想像我这样沮丧的人,他们只是谷歌如何摆脱这种行为,而不幸的是,现在没有足够的时间学习“为什么”,这仍然很有趣。
doc_id 2015年

4
当我的终端出现故障时,像这样,我使用reset代替,stty sane之后它可以正常工作。
Capi Etheriel 2015年

137

(从grawity的解释,那下面就xargsstdin/dev/null。)

解决方案针对此问题是将增加-o参数xargs。来自man xargs

-o

      /dev/tty在执行命令之前,请像子进程 一样重新打开stdin 。如果要xargs运行交互式应用程序,这将很有用。

因此,以下代码行应为您工作:

找 。名称“ * .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的所有参数。)


2
您将如何以此创建bash别名?$@似乎没有正确翻译参数。
zanegray 2015年

1
@zanegray-您不能创建别名,但是可以使其成为函数。试试:function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
Christopher

有关GNU xargs解决方案的工作原理以及为什么需要虚拟ignoreme字符串的详细说明,请参见vi.stackexchange.com/a/17813
wisbucky,

@zanegray,您可以将其设为别名。引号很棘手。请参阅vi.stackexchange.com/a/17813中的
wisbucky

The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.(对我而言,它在macOS上不可用,因为我是从homebrew(GNU版本)安装xargs的)
localhostdotdev,

33

最简单的方法:

vim $(find . -name "*foo*")

5
主要问题是“为什么”,而不是“如何避免”,并且两年半前人们对它的回答感到满意。
DevSolar 2014年

5
当然,这在文件名包含空格或其他特殊字符时无法正常工作,并且也存在安全风险。
Dejay Clayton 2015年

1
我最喜欢的答案,因为它适用于列出文件的每个命令,而不仅仅是“查找”或通配符。正如Dejay指出的,确实需要一点信任。
特拉维斯·威尔逊

1
xargs不适用于许多用例,例如,当路径数量非常高时(cc @TravisWilson)
好人

21

如果在find上使用-exec选项,而不是通过管道传递到xargs,它应该可以正常工作。

find . -type f -name filename.txt -exec vi {} + 

2
呵呵……有一种技巧+(而不是“通常的” \;)将所有找到的文件放入一个 Vim会话中-我一直忘了这个选择。您当然是对的,为此+1。我vim $(find ...)只是出于习惯而使用。但是,我实际上是在问为什么管道操作会拧紧端子,而grawless则用他的解释来说明这一点。
DevSolar

2
这是最好的答案,并且可以在BSD / OSX / GNU / Linux上使用。
kevinarpe 2014年

1
同样,查找不是获取必须由vim同时编辑的文件列表的唯一方法。我可以使用grep查找带有模式的所有文件,然后尝试同时编辑它们。
Chandranshu 2014年

8

改用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


1
并非无处不在。大部分时间,我都在不随意安装其他工具的服务器上工作。但是还是谢谢你的提示。
DevSolar 2011年

如果您可以自由地执行'cat> file; chmod + x file”,则可以安装GNU Parallel:这只是一个perl脚本。如果需要手册页等,可以将其安装在homedir下:./configure --prefix = $ HOME && make && make install
Ole Tange

2
OK,试过了-但平行并不能打开所有的文件,它打开它们连续。一个简单的操作也很方便。vim $(find . -name "*.txt")更简单,您可以一次打开所有文件。
DevSolar 2011年

5
@DevSolar:有点风马牛不相及,但两者find | xargs$(find)会在文件名称空间大的问题。
grawity 2011年

2
@grawity正确,但是没有容易解决的方法(据我所知)。你不得不开始乱搞$IFS-print0和东西,然后你离开一个一次性的命令行解决方案的领域,并达到了一个点,你应该拿出一个剧本......还有就是为什么在文件名中的空格气馁的理由。
DevSolar 2011年

0

也许不是最好的,但这里是我使用的脚本(名为vim-open):

#!/usr/bin/env ruby

require 'shellwords'

inputs = (ARGV + (STDIN.tty? ? [] : STDIN.to_a)).map(&:strip)
exec("</dev/tty vim #{inputs.flatten.shelljoin}")

将与vim-open a b cls | vim-open例如


至于其他几个答案,请注意实际问题是“为什么”,而不是“如何避免”。(对此,我仍将在我的问题下指向Trevor的评论,这是不需要脚本,别名或任何东西的最可靠的方式。)
DevSolar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.