xargs和vi-“输入不是来自终端”


14

我的php.ini系统上遍布整个系统,大约有10个文件,我想快速浏览它们。我尝试了以下命令:

locate php.ini | xargs vi

但是vi警告我Input is not from a terminal,然后控制台开始变得很奇怪-之后,我需要按:q!退出vi,然后从ssh会话断开连接,然后重新连接以使控制台再次正常运行。

我认为我有点了解这里发生的事情-基本上,该命令在vi启动时尚未完成,因此该命令可能尚未完成,vi并且不认为终端处于正常模式。

我不知道如何解决它。我搜索过Google以及运气不好的unix.stackexchange.com。



附带一提,您可以reset在终端损坏时运行以重置终端(不必从ssh会话断开连接)。
wisbucky

Answers:


12
vi $(locate php.ini)

注意:如果您的文件路径包含空格,则会出现问题,但是在功能上等同于您的命令。
下一个版本将正确处理空格,但会更加复杂(尽管文件名中的换行符仍然会破坏空格

(IFS=$'\n'; vi $(locate php.ini))


说明:

发生的情况是程序从产生它们的过程中继承了它们的文件描述符。xargs将其STDIN连接到的STDOUT locate,因此vi不知道原始STDIN真正位于什么位置。


2
xargs很棒,是我最喜欢的工具之一-它不适合与将stdin用于数据提要以外的程序一起使用。我更喜欢您的回答和您的解释,所以无论如何+1 :)
cas

@CraigSanders我不喜欢它,因为它太容易被滥用(使用不当)并最终损坏。我从来没有遇到过任何我绝对必须使用的东西,xargs而这是直接用shell(或find)无法完成的。但是,我认为这将是最佳解决方案。因此,只要您了解xargs正在执行的操作,如何拆分参数,如何运行程序等,以及如何正确使用它,我就会说::-P
Patrick

它不能被诸如... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(将字段3的所有值相加)之类的东西击败。或sed -e 's/ /|/g'用于构建正则表达式。是的,就像任何工具一样,您确实需要知道如何使用它,以及它的局限性和警告。
cas

vi $(...)方法还存在一个问题,除了之外的其他shell中都有通配符zsh
StéphaneChazelas

还要注意,使用xargs空格问题之外的方法,单引号,双引号和反斜杠的文件名也是一个问题。
斯特凡Chazelas

10

先前已在“ 超级用户”论坛上提出了这个问题。

引用@grawity关于该问题的答案:

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

Vim期望其stdin与它的控制终端相同,并直接在stdin上执行各种与终端相关的ioctl。当在/ dev / null(或任何非tty文件描述符)上完成时,这些ioctl是没有意义的,并返回ENOTTY,它会被静默忽略。

xarg的手册页中提到了这一点。从OSX / BSD:

-o在执行命令之前,在子进程中以/ dev / tty重新打开标准输入。如果您希望xargs运行交互式应用程序,这将很有用。

因此,在OSX上,可以使用以下命令:

find . -name "php.ini" | xargs -o vim

虽然在GNU版本上没有直接切换,但是此命令将起作用。(请确保包含dummy字符串,否则它将删除第一个文件。)

find . -name "php.ini" | xargs bash -c '</dev/tty vim "$@"' dummy

以上解决方案由SuperUser上的Jaime McGuigan提供。将它们添加到此处,供以后在站点中搜索此错误的所有访问者使用。


3
+1感谢-o提示。我已经使用xargs多年了,却从未注意到。...只是检查了系统上的手册页,那是因为它不是GNU xargs功能。手册页的确提供xargs sh -c 'emacs "$@" < /dev/tty' emacs了它们所声称的功能,因为它们声称是一种更灵活和可移植的替代方法(尽管GNU倾向于可移植性而不是功能,这很有趣。)
cas

2

使用GNU findutils和支持进程替换的shell(ksh,zsh,bash),您可以执行以下操作:

xargs -r0a <(locate -0 php.ini) vi

想法是通过-a filename而不是stdin 传递文件列表。使用-0可以确保无论文件名包含什么字符或非字符都可以使用。

使用zsh,您可以执行以下操作:

vi ${(0)"$(locate -0 php.ini)"}

(其中0是要在NUL上拆分的参数扩展标志)。

但是请注意,如果未找到文件,则与此相反的情况xargs -r是仍然vi不带参数地运行。


0

在同一编辑器中编辑多个php.ini?

尝试: vim -o $(locate php.ini)


0

调用vim并将其连接到前一个管道的输出而不是终端时,会发生此错误,并且它接收到不同的意外输入(如NUL)。当您运行以下命令时vim < /dev/null,也会发生同样的情况,因此reset这种情况下的命令会有所帮助。超级用户粗俗解释可以很好地解释这一点。

在Unix / OSX可以使用xargs-o参数,如:

locate php.ini | xargs -o vim

-o在执行命令之前,在子进程中以/ dev / tty重新打开stdin。如果您希望xargs运行交互式应用程序,这将很有用。

在Linux上,请尝试以下解决方法:

locate php.ini | xargs -J% sh -c 'vim < /dev/tty $@'

或者,使用GNU parallel代替xargs强制tty分配,例如:

locate php.ini | parallel -X --tty vi

注意:parallel在Unix / OSX上,它具有不同的参数,并且不支持tty,因此无法使用。

许多其他流行的命令也提供了伪tty分配(如-t中的ssh),因此请查看帮助。

或者使用find传递文件名进行编辑,因此不需要xargs,只需使用即可-exec,例如:

find /etc -name php.ini -exec vim {} +

0

@Patrick的IFShack仅对于像bash和这样的笨蛋壳是必需的zshfish 默认情况下在换行符上拆分字符串。

$ vim (locate php.ini)

如果我们中只有一个人实际上拥有一个名称中带有换行符的文件,那么上帝会帮助我们所有人。使用Linux 17年后,我什至从未见过。对于那些无论如何都必须工作的脚本,我只想在文件名中加上换行符就可以了,但是类似的脚本可能无法交互运行vim。


zsh默认情况下在SPC,TAB,NL和NUL上拆分。与之相比,它没有做的事情bash是对结果执行globing,因此文件名中的通配符不是问题。在中zsh,您将IFS=$'\0'; vi $(locate -0 php.ini)按照我在答案中所示的那样进行操作,或者使用我在答案中所示vi ${(0)"$(locate -0 php.ini)"}的显式拆分运算符。另外要注意的tcsh的vi "`locate php.ini`"
斯特凡Chazelas

糟糕 好的,这行得通:$ f='not there'<ret>$ ls $f<ret>但这不行:ls echo not there。好的,我需要对此进行一些更新。
enigmaticPhysicist

是的,当您执行zsh时,做的事情不正确ls "$(echo test; echo other test)"。只有鱼才能做正确的事。
enigmaticPhysicist

假设您的意思是没有引号的意思是一样的,那不是“正确的”,而是分裂成行,这只是一个不同的选择。zsh默认情况下会按字分割(像所有其他shell一样),并且可以告诉zsh通过$IFS或通过显式运算符(f0参数扩展标志)在行或NUL上进行分割。对于任意文件名,按单词拆分或按行拆分同样是错误的,您需要在NUL上拆分或解析某些编码,这fish是不能做到的。在zsh,这IFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")ls -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
斯特凡Chazelas

嗯 在换行符上分割就足够了。就像答案说的那样,文件名中的换行符非常少见。我从没看过17年以来发生的事情。和换行符相比,nul更方便。
enigmaticPhysicist

0

一个快速的方法来做到这一点,假设你能保证没有一个文件路径包含SPC,TAB,NL, ,,* 字符(也和一些贝壳)是使用反单引号(又名重音符)前执行命令另一个命令正在运行。?[\{...}

例如

vi `find / -type f -name 'php.ini'`

反引号中包含的命令将首先执行。然后,通过反引号之前所述的命令来执行所包含命令的输出。

例如,在上面的行中,该find / -type f -name 'php.ini'命令将首先执行,发送输出,然后vi在应用于该输出的split + glob的结果上执行该命令。


3
反引号对于单引号来说很容易混淆。使用$(find ...)代替。
cas

1
猜猜这还会在文件名中的空格和/或换行符处中断吗?
cwd 2012年

这是在bash脚本中执行shell命令的方式。我从来没有在脚本中的空格或换行符上有任何中断,也没有在过不到的情况下使用过任何一行。但是,我从未尝试vi使用此方法打开多个文件。根据vi读取和执行输出的方式,很可能会在新行或空白处打断。
tacotuesday,2012年
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.