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

回答:


12
vi $(locate php.ini)

注:ファイルパスにスペースが含まれている場合、これには問題がありますが、機能的にはコマンドと同等です。
この次のバージョンはスペースを適切に処理しますが、少し複雑です(ただし、ファイル名の改行はそれでも壊れます)

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


説明:

何が起こっているのかというと、プログラムはそれらを生成したプロセスからファイル記述子を継承しています。xargsSTDINがのSTDOUTに接続されているlocateためvi、元のSTDINが実際に何であるかについての手掛かりはありません。


2
xargsは素晴らしいです。私のお気に入りのツールの1つです。stdinをデータフィード以外に使用するプログラムでの使用には適していません。私はあなたの答えとそれ以外の説明が好きなので、とにかく+1します:)
cas

@CraigSanders悪用(不適切に使用)して簡単に壊れるので、私はそれが好きではありません。xargsシェル(またはfind)で直接行うことができないために絶対に使用しなければならなかったものに出くわしたことはありません。しかし、それが最善の解決策となるケースを考えることができます。だから、あなたが何xargsをしているのか、それが引数をどのように分割するのか、どのようにプログラムを実行するのかなどを理解し、それを適切に使用している限り、私はそれのために行くと思います:-P
Patrick

... | awk '{print $3}' | xargs | sed -e 's/ /+/g' | bc(フィールド3のすべての値を合計するなど)には勝てません。またはwith sed -e 's/ /|/g'正規表現を作成します。そして、はい、他のツールと同様に、その使用方法と、その制限と注意事項を知る必要があります。
cas

このvi $(...)アプローチには、以外のシェルでのワイルドカードの問題もありzshます。
ステファンChazelas

またxargs、空白の問題以外のアプローチでは、一重引用符、二重引用符、バックスラッシュを含むファイル名も問題になります。
ステファンChazelas

10

この質問は、以前にスーパーユーザーフォーラムで質問されています。

その質問に対する@grawityの回答から引用すると:

xargsを介してプログラムを呼び出すと、プログラムのstdin(標準入力)は/ dev / nullを指します。(xargsは元の標準入力を認識しないため、次善の策を実行します。)

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

これは、xargのマニュアルページで言及されています。OSX / BSDから:

-oコマンドを実行する前に、子プロセスでstdinを/ 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
-oヒントに+1をありがとう。私は何年もxargsを使用してきましたが、それに気付いたことはありません....私のシステムのmanページを確認したところ、それはGNU xargs機能ではないためです。xargs sh -c 'emacs "$@" < /dev/tty' emacs彼らが主張するものはより柔軟で移植可能な代替物であるので、manページは提供します(ただし、GNUが機能よりも移植性を好むのはちょっと面白いですが:)。
cas

2

GNU findutilsと、プロセス置換をサポートするシェル(ksh、zsh、bash)を使用すると、次のことができます。

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

-a filenamestdinではなくファイルリストを介してファイルリストを渡すというアイデアです。を使用-0すると、ファイル名に含まれている可能性のある文字や非文字に関係なく機能します。

を使用するとzsh、次のことができます。

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

(ここ0で、NULで分割するパラメーター拡張フラグです)。

ただし、これとは逆にxargs -rviファイルが見つからない場合でも引数なしで実行されます。


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 $@'

別の方法として、tty割り当てを強制parallelするのではなく、代わりにGNU を使用しますxargs

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

注:parallelUnix / OSXでは、パラメータが異なり、ttyをサポートしていないため、機能しません。

ウェル(などの他の多くの人気のあるコマンドは、仮想端末の割り当てを提供-tssh)、その助けをチェック。

またはfind、編集するファイル名を渡すために使用するのでxargs、必要はありません。-execたとえば、次のように使用します。

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

0

@PatrickのIFSハックはbashandのようなダムシェルにのみ必要ですzshfish デフォルトでは、文字列を改行で分割します。

$ vim (locate php.ini)

そして、もし私たち一人がその名前に改行を含むファイルを実際に持っているなら、神は私たち全員を助けてくださいます。Linuxを17年間使用して以来、一度も目にしたことはありません。私は、何があっても機能する必要のあるスクリプトについて、改行を含むファイル名をサポートすることだけを気にしますが、そのようなスクリプトはおそらくvimをインタラクティブに実行していません。


zshデフォルトでは、SPC、TAB、NL、NULで分割されます。比較しないことはbash、結果に対してグロビングを実行することです。そのため、ファイル名のワイルドカード文字は問題になりません。では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はデフォルトで(他のすべてのシェルと同様に)単語を分割し、$IFS明示的な演算子(fおよび0パラメーター展開フラグ)を介して、または明示的な演算子を介して、行またはNULで分割するように指示できます。任意のファイル名の場合、単語による分割または行による分割も同様に間違っています。NULで分割するか、エンコードを解析する必要がありますが、これfishはできません。でzsh、それはIFS=$'\0'; ls -ld -- $(printf '%s\0' "$file1" "$file2")ls -ld -- ${(0)"$(printf '%s\0' "$file1" "$file2")"}
StéphaneChazelas

ええ。改行で分割するだけで十分です。答えが言うように、ファイル名の改行は非常にまれです。私は文字通り17年間でそれが起こるのを見たことがない。そして改行はヌルよりずっと便利なセパレータです。
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

これは、bashスクリプトでシェルコマンドを実行する方法です。スクリプトのスペースや改行で改行したり、1つのライナーで使用したりしたことがありません。ただし、viこの方法を使用して複数のファイルを開こうとしたことはありません。vi出力の読み取り方法と実行方法によっては、改行またはスペースで改行する可能性があります。
tacotuesday
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.