私は次のことをしようとしています:
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
grep
1回の呼び出しですべての正規表現を見つけることもできます。
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
。
旗銀-f
POSIXで指定したがって、携帯性は非常に良いです。