Xargsはパイプラインの2番目の側面に入りますか?

Xargsはパイプラインの2番目の側面に入りますか?

私は次のことをしようとしています:

cat file1.txt | xargs -I{} "cat file2.txt | grep {}"

file1 の各行は、3 番目のパイプの終わりの grep 値になると予想します。期待どおりに動作しません。

-I{}パイプラインに達すると代替品の検索が中断されるためですか?解決策はありますか?

答え1

これは、パイプを作成したりリダイレクトするにはシェルが必要なためです。これはcat接続コマンドなので、1つのファイルにのみ使用することはほとんど意味がありません。

cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}

するいいえする:

cat file1.txt | xargs -I{} sh -c 'cat file2.txt |

これは、コマンド注入の脆弱性と同じであるためです。{}codeパラメータのshシェルコードに展開されます。たとえば、行がある場合にfile1.txt呼び出され$(reboot)ますreboot

-eまたは--)も重要です。これがなければ、で始まる正規表現に問題があります-

代わりに、リダイレクトを使用して上記の内容を簡素化できますcat

< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}

または、リダイレクトを使用する代わりに、ファイル名を引数として渡すこともできますgrep。この場合、以下を削除することもできますsh

< file1.txt xargs -I{} grep -e {} file2.txt

grep1回の呼び出しですべての正規表現を見つけることもできます。

grep -f file1.txt file2.txt

ただし、この場合は1行に1つの正規表現があり、file1.txt特別な引用は行われませんxargs

xargsデフォルトでは、入力は空のリスト(一部の実装ではスペースとタブのみ、他の実装では[:blank:]現在のロケールの文字クラスにあるすべての文字)、またはバックスラッシュダッシュと一重引用符と二重引用符を含む改行で区切られた単語として扱われます。区切り文字(改行はバックスラッシュでのみエスケープできます)または互いをエスケープするために使用されます。

たとえば、次のような入力の場合:

 'a "b'\" "bar baz" x\
y

xargs何も-I{}渡されずにa "b"注文さbar bazれますx<newline>y

を使用すると、-I{}1xargs行に1つの単語が得られますが、いくつかの追加処理は引き続き実行されます。先行(末尾ではない)スペースは無視します。空白はもはや区切り文字とは見なされませんが、見積もり処理は進行中です。

上記の入力はxargs -I{}コマンドに引数を渡します。a "b" foo bar x<newline>yさらに、POSIX の要件により、単語の長さが 255 文字を超えると、多くのシステムは機能しません。だいたいかなりxargs -I{}役に立たない。

各行をコマンドの引数としてそのまま渡すには、GNUxargs -d '\n'拡張を使用できます。

< file1.txt xargs -d '\n' -n 1 grep file2.txt -e

(これはgrepオプションが引数の後に渡されるか(POSIXでは正しいオプションが環境に存在しない場合)、移植可能にするGNUの別の拡張に依存します。

sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
  for line do
    grep -e "$line" file2.txt
  done' sh

あなたが望むなら、すべて言葉それぞれの代わりにfile1.txt(引用符はまだ認識されます)ワイヤー検索するには(1行に単語が1つだけあり、末尾の空白の問題も解決される場合)、次のxargs -n1代わりに単独で使用できます-I

< file1.txt xargs -n1 sh -c '
  for word do
    grep -e "$word" file2.txt
  done' sh

先行スペースと末尾のスペースを削除するには(引用符なし)、次のようにすることxargsもできます。

unset IFS # restore word splitting to its default
while read -r regexp; do
  grep -e "$regexp" file2.txt
done < file1.txt

答え2

実行したい作業によっては、xargs完全にスキップして次の解決策を使用する方が良いかもしれません。

grep -f file1.txt file2.txt

これは元のコマンドとは異なります(Stéphane Chazelasの回答に従って修正した場合)

  • file2.txt一致するパターンに関係なく、表示される順序で行が印刷されます。コマンドでは、最初のパターンに一致するすべての行が印刷され、次に2番目のパターンに一致するすべての行が印刷されます。
  • 複数のパターンに一致する行は一度だけ印刷されます。コマンドは、一致するパターンごとに一度印刷されます。
  • -vとを含む複数のフラグを簡単に使用できます-c

旗銀-fPOSIXで指定したがって、携帯性は非常に良いです。

関連情報