it-swarm-ja.com

findを介してviを呼び出す| xargsが端末を壊します。どうして?

次のようにvimからfind | xargsを呼び出す場合:

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

あなたはについての警告を受け取ります

Input is not from a terminal

あとはかなり動作がおかしい端末。 なぜそれは何ですか?


この質問は、明示的にwhyについてであり、回避方法についてではありませんでした。これは尋ねられ、答えられた 他の場所

140
DevSolar

xargsを介してプログラムを呼び出すと、プログラムのstdin(標準入力)は_/dev/null_を指します。 (xargsはoriginal stdinを知らないため、次善の策です)

 $ true | xargs  filan  -s 
 0 chrdev /dev/null
 1 tty /dev/pts/1
 2 tty/dev/pts/1 
 
 $ true | xargs ls -l /dev/fd/

Vimはそのstdinが制御端末と同じであることを期待し、さまざまな端末関連のioctlをstdinで直接実行します。 _/dev/null_(またはtty以外のファイル記述子)で実行すると、それらのioctlは無意味になり、ENOTTYを返しますが、これは黙って無視されます。

  • より具体的な原因についての私の推測:起動時にVimは古い端末設定を読み取って記憶し、終了時にそれらを復元します。私たちの状況では、tty以外のfd(ファイル記述子)に対して「古い設定」が要求されると、Vimはすべての値を空にしてすべてのオプションを無効にし、不用意に端末に設定します。

    これは、_vim < /dev/null_を実行して終了し、次にsttyを実行することで確認できます。これにより、_<undef>_ sが大量に出力されます。 Linuxで_stty sane_を実行すると、端末が再び使用可能になります(ただし、willは_iutf8_などのオプションを失い、後で軽度の不快感を引き起こす可能性があります) 。

can端末制御用に_/dev/tty_を開くため、これはVimのバグと考えることができますが、そうではありません。 (起動時のある時点で、Vimはstderrをstdinに複製します。これにより、書き込み用に開かれたfdから入力コマンドを読み取ることができますが、それでも十分に早くは行われません。)

104
user1686

(大雑把な説明に続き、xargsstdin/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 "[email protected]"' ignoreme

ignoremeは$ 0を占めるために存在するため、$ @はすべてxargsからの引数です。)

141
James McGuigan

最も簡単な方法:

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

Xargsにパイプするのではなく、findで-execオプションを使用すると、問題なく動作します。

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

代わりに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

8
Ole Tange

多分最高ではないかもしれませんが、ここで私が使用するスクリプト(名前は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 cおよびls | vim-openで動作します

0
localhostdotdev