「xargs grep」は何をしますか?


25

私はgrepコマンドを知っていて、の機能について学んxargsでいるので、このページを読んで、xargsコマンドの使用方法の例をいくつか示します。

最後の例、例10に混乱しています。「xargsコマンドはgrepコマンドを実行して、文字列 'stdlib.h'を含むすべてのファイルを検索します(findコマンドで提供されたファイル)」

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

ただし、単に使用することの違いは何ですか

$ find . -name '*.c' | grep 'stdlib.h'

明らかに、私はまだxargsが正確に何をしているかに苦労しているので、どんな助けも大歓迎です!


2
この質問は役に立つかもしれません:XArgsはいつ必要ですか?
TheOdd

回答:


30
$ find . -name '*.c' | grep 'stdlib.h'

これは、出力(stdout)*からfind(stdin of)* grep 'stdlib.h' にテキストとしてパイプします(つまり、ファイル名はテキストとして扱われます)。grep通常の処理を行い、このテキスト内の一致する行(パターンを含むファイル名)を見つけます。ファイルの内容は読み取られません。

$ find . -name '*.c' | xargs grep 'stdlib.h'

これは、それぞれの結果が引数であるコマンド grep 'stdlib.h'を構築しfindます-そのため、これは、見つかった各ファイルで一致するものを探しますfindxargsその標準入力を指定されたコマンドの引数に変えると考えることができます)*

-type ffindコマンドで使用するかgrep、一致するディレクトリからエラーが発生します。また、ファイル名にスペースがある場合は、xargsひどく台無しなので、追加することにより、ヌル区切り文字を使用する-print0と、xargs -0より信頼性の高い結果を得るため:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* @ catのコメントで示唆されているように、これらの追加の説明ポイントを追加


2
(省略しているように見えるので)grepの引数とは異なるgrepのstdinにstdout|パイプし、混乱を招く結果をもたらす重要な点に注意することを検討してください。

1
または、GNU findのを使用しますfind -name '*.c' -exec grep stdlib.h {} +。実際にxargsを使用することはほとんどありません。また、xargsがgrep $(find)コマンド置換に似た目的を果たしていると誰も言及していないので驚いたので、私は自分の答えを書きました。xargsを制限が少なく問題の少ないコマンド置換として説明するのは自然なことです。
ピーターコーデス

xargsを使用する1つの状況は、findの結果として多くのファイルを削除する場合です。-exec rmを実行すると、各ファイルでrmが1つずつ実行されますが、これは非常に非効率的です。xargsへのパイピングは、1つのrmですべてを一度に実行します。say -n50(一度に50ずつ)で制限すると、コマンドラインのオーバーフロー(多くのファイルの問題)を防ぐことができます。
-lsd

1
@lsd:find -deleteその特別な場合はどうしてですか?または、以外のコマンドの場合rm、GNU findがある場合は、-exec some_command {} +xargsのようなバッチにグループ化し、代わり\;にコマンドを個別に実行する動作を行います。
ピーターコーデス

@lsd findは、-exec command \;両方xargsを使用している場合にのみ各ファイルでコマンドを実行-exec command \+し、システムで許可されている最大数の引数でコマンドを呼び出します。言い換えれば、彼らは同等だ
Sergiy Kolodyazhnyy

6

xargsは標準入力を受け取り、コマンドライン引数に変換します。

find . -name '*.c' | xargs grep 'stdlib.h' に非常に似ています

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

ファイル名のリストが単一のコマンドラインに対して長すぎない限り、同じ結果が得られます。(Linuxは1つのコマンドラインでメガバイトのテキストをサポートしているため、通常はxargsは必要ありません。)


ただし、ファイル名にスペースが含まれていると壊れるので、これらは両方とも悪いです。代わりにfind -print0 | xargs -0動作しますが、動作します

find . -name '*.c' -exec grep 'stdlib.h' {} +

ファイル名がどこにもパイプされることはありません。ファイル名をfind大きなコマンドラインにまとめてgrep直接実行します。

\;+ファイルごとにgrepを個別に実行する代わりに、はるかに遅くなります。しないでください。ただし+、これはGNU拡張機能であるため、xargsGNUが見つからないと想定できない場合は、これを効率的に行う必要があります。


あなたは除外した場合xargsfind | grepそのファイル名のリストに対してそのパターンマッチングを行いfind印刷します。

したがって、その時点で、同様に行うことができますfind -name stdlib.h。もちろん、を使用すると-name '*.c' -name stdlib.h、これらのパターンは両方とも一致することができず、findのデフォルトの動作はルールをANDするため、出力は得られません。

lessプロセスの任意の時点で置き換えて、パイプラインの一部が生成する出力を確認します。


さらなる参考資料http : //mywiki.wooledge.org/BashFAQには素晴らしいものがあります。


1
GNU xargs -dは区切り文字も設定する必要があるため-d'\n'、改行で区切られたリストを処理するために使用できます。これは、ファイル内のファイル名のリストを処理する場合などに役立ちます(ファイル名がそれらの改行、つまり)
ilkkachu

@ilkkachu:ええ、ファイル名の改行はほとんどのスクリプトを壊すため、スペースよりもはるかにまれです。 myfunc(){ local IFS=$'\n'; fgrep stdlib.h` $(find); }も同じ効果で動作します。または、ワンライナーとして、(IFS=...; cmd...)サブシェルはIFSへの変更を保存/復元することなく含むように機能します。
ピーターコーデス

@PeterCordes command $( find )タイプのものはしないでください。スペースと特殊文字を含む問題のあるファイル名は、このタイプのものを壊す可能性があります。最低でもコマンド置換を二重引用符で囲んでください。
セルギーKolodyazhnyy

@SergiyKolodyazhnyy:それを実際に推奨しているように見えることを指摘してくれてありがとう。スキミングする人は、次のセクションを読む代わりにそれをコピー/ペーストした可能性があります。それに対処するために更新されました。
ピーターコーデス

@SergiyKolodyazhnyy:それとも私のコメントに返信しましたか?IFSを使用するのと同じように設定していることに注意してくださいxargs '-d\n'。グローブの展開とシェルメタキャラクターの処理は、コマンド置換の効果の前に行われるため、$()またはを含むファイル名でも安全だと思います>。コマンド置換での単語分割の使用は、ファイル名について何かを知っている1回限りのインタラクティブな使用を除いて、良い習慣ではないことに同意しました。しかし、command "$(find)"あなたは...それは正確に1つのファイル名を生成することが予想される場合にのみ有効です
ピーター・コルド

5

一般に、1つのコマンドから別のコマンド()に何かxargsをパイプする場合に使用されますが、最初のコマンドからの出力は2番目のコマンドの入力として正しく受信されません。|Command1 | Command2

これは通常、2番目のコマンドが標準入力(stdin)を介したデータ入力を正しく処理しない場合に発生します(例:入力としての複数の行、行の設定方法、入力として使用される文字、入力としての複数のパラメーター、受信したデータ型入力など)。簡単な例を示すには、次をテストします。

例1:

ls | echo- echo受信した入力の処理方法がわからないため、これは何もしません。ここで、この場合、それを使用xargsすると、次のように正しく処理できる方法で入力が処理されますecho(例:1行の情報として)

ls | xargs echo-これにより、すべての情報lsが1行で出力されます

例2:

goというフォルダー内に複数のgoLangファイルがあるとします。私は次のようなものでそれらを探します:

find go -name *.go -type f | echo-しかし、パイプ記号がそことecho最後にある場合、それは機能しません。

find go -name *.go -type f | xargs echo-ここでは、おかげで機能しますxargsが、findコマンドからの各応答を1行にしたい場合は、次のようにします。

find go -name *.go -type f | xargs -0 echo-この場合、からの同じ出力findが表示されechoます。

などのコマンドをcp, echo, rm, less使用して入力を処理するためのより良い方法が必要な場合は、xargs


4

xargs ファイルのリストに基づいて(通常)コマンドライン引数を自動生成するために使用されます。

したがって、followoing xargsコマンドの使用に代わるいくつかの方法を検討します。

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

他の回答で元々言及されていなかった他のオプションの代わりに使用する理由はいくつかあります。

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\;grepファイルごとに1つのプロセスを生成します。これは一般に悪い習慣と見なされ、多くのファイルが見つかった場合にシステムに大きな負荷をかける可能性があります。
  2. 多くのファイルがある場合grep 'stdlib.h' $(find . -name '*.c')$(...)操作の出力がシェルのコマンドラインの最大長を超えるため、コマンドが失敗する可能性があります。

他の回答で述べたように、このシナリオでの引数とxargs の-print0引数を使用する理由は、特定の文字(引用符、スペース、改行など)を含むファイル名が引き続き正しく処理されるようにするためです。find-0

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.