findを使用してviを呼び出す| xargsは私の端末を壊します。どうして?


137

vim介して呼び出す場合find | xargs、次のようになります。

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

次に関する警告が表示されます

Input is not from a terminal

その後、動作がほとんど壊れた端末。何故ですか?


11
サイドノート:この操作はvim内で完全に実行でき、使用しないfindか、まったく実行できませんxargs。引数なしでvimを開き:args **/*.txt<CR>、エディター内からvimの引数を設定するために実行します。
トレバーパウエル

3
@TrevorPowell:これらすべての年月の間、vimは私を驚かせることを止めませんでした。
-DevSolar



回答:


100

を介してプログラムを呼び出すxargsと、プログラムの標準入力(標準入力)はを指し/dev/nullます。(xargsは元の 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が制御端末と同じであることを期待し、stdinでさまざまな端末関連のioctlを直接実行します。/dev/null(またはtty以外のファイル記述子)で行われた場合、それらのioctlは無意味であり、ENOTTYを返します。これは静かに無視されます。

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

    これを実行vim < /dev/nullするには、を実行して終了し、次にを実行してstty、大量のを出力します<undef>。Linuxでは、実行stty saneするとターミナルが再び使用可能になります(ただし、などのオプションは失われますが、後で多少の煩わし生じるiutf8可能性があります)。

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


20
+ 1、TLの場合、DRの人はただ走るstty sane
doc_id

@rahmanisback:他の回答に加えて、Trevorのコメントはすべて、そもそも端末の破損を回避する方法を提供しました。それはによってカバーされています-私の質問は「なぜ」ではなく、「どのように回避する」だったので、私は、grawityの答えを受け入れた別の質問、実際に生み出された、このいずれかを。
DevSolar

@DevSolar理解しましたが、残念なことに、「なぜ」を勉強するのに十分な時間がないにもかかわらず、その行動を取り除く方法をグーグルで探している私のような欲求不満な人について考えてください。
doc_id

4
このように端末が壊れると、reset代わりに使用し、stty saneその後は正常に動作します。
カピエテリエル

137

(grawityの説明、に続いてxargsポイントstdin/dev/null。)

この問題の解決策は、-oパラメータをに追加することxargsです。からman xargs

-o

      /dev/ttyコマンドを実行する前に、子プロセスの ように標準入力を再度開きます。これは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からのすべての引数です。)


2
これからbashエイリアスをどのように作成しますか?$@引数を正しく翻訳していないようです。
zanegray

1
@zanegray-エイリアスを作成することはできませんが、関数にすることはできます。試す:function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
クリストファー

GNU xargsソリューションの仕組みとダミーignoreme文字列が必要な理由の詳細については、vi.stackexchange.com
a /

@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.(homebrew(GNUのもの)からxargsをインストールしたため、macOSでは利用できませんでした)
localhostdotdev

33

最も簡単な方法:

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

5
主な質問は「なぜ」であり、「それを避ける方法」ではなく、2年半前に満足のいく回答が得られました。
DevSolar 14年

5
もちろん、これはファイル名にスペースまたは他の特殊文字が含まれる場合は適切に機能せず、セキュリティ上のリスクでもあります。
デジェイクレイトン

1
私のお気に入りの答えは、「検索」やワイルドカードだけでなく、ファイルを一覧表示するすべてのコマンドで機能するためです。Dejayが指摘しているように、少し信頼が必要です。
トラビスウィルソン

1
これは、xargsが設計されている多くのユースケースでは機能しません。たとえば、パスの数が非常に多い場合(cc @TravisWilson)
Good Person

21

xargsにパイピングするのではなく、findで-execオプションを使用すれば、うまく動作するはずです。

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

2
ええと...そこにあるトリックは、+(「通常」の代わりに\;)見つかったすべてのファイルを1つの Vimセッションに入れることです-私忘れがちなオプションです。もちろん、あなたは正しいです、そしてそのために+1。私はvim $(find ...)単に習慣から外れて使用します。しかし、私は実際にパイプ操作がなぜターミナルをねじ止めするのを尋ねていました。
-DevSolar

2
これが最良の回答であり、BSD / OSX / GNU / Linuxの両方で機能します。
ケビナープ14

1
また、vimで同時に編集する必要があるファイルのリストを取得する方法は、findだけではありません。grepを使用して、パターンを持つすべてのファイルを検索し、それらを同時に編集することもできます。
Chandranshu 14年

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

あなたが 'cat> fileを行う自由がある場合; chmod + xファイル」を選択すると、GNU Parallelをインストールできます。これは単にperlスクリプトです。マニュアルページなどが必要な場合は、homedirにインストールできます。./configure --prefix = $ HOME && make && make install
Ole

2
OK、それを試してみました-しかし、並列がないではないすべてのファイルを開くには、それらを開くし続けて。また、簡単な操作には非常に手間がかかります。vim $(find . -name "*.txt")より簡単で、すべてのファイルを一度に開くことができます。
DevSolar

5
@DevSolar:やや無関係が、両方find | xargs$(find)ファイル名にスペースを持つ大きな問題を持っています。
grawity

2
@grawity正しいが、それを回避する簡単な方法はない(知っている)。あなたはをいじる開始する必要があるだろう$IFS-print0とか、その後、あなたはワンショットコマンドラインソリューションの領域を残して、あなたはスクリプトを考え出す必要がある点に達し...ファイル名にスペースが推奨された理由があります。
-DevSolar

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 cなどls | vim-openで動作します


他のいくつかの回答については、実際の質問は「なぜ」、「それを避ける方法」ではなかったことに注意してください。(これについては、スクリプト、エイリアスなどを必要としない最も堅実な方法として、私の質問の下でトレバーのコメントを指摘します。)
DevSolar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.