xargsが最後の引数を処理しないのはなぜですか?

xargsが最後の引数を処理しないのはなぜですか?

観察する:

mark@L-R910LPKW:~$ echo a b | xargs -d' ' -I{} bash -c 'echo {} 1'
a 1
b
bash: line 2: 1: command not found
mark@L-R910LPKW:~$

どうしたの?

答え1

何かが間違っています。

b出力に表示されるので処理されていますが、期待した方法ではありません。

最初のステップとして、bashが何を見るかを教えてくれます。-xトレースを有効にするオプションを渡します。

$ echo a b | xargs -d' ' -I{} bash -x -c 'echo {} 1'
+ echo a 1
a 1
+ echo b
b
+ 1
bash: line 2: 1: command not found

echo a 1したがって、期待どおりにbashが最初に呼び出されます。しかし、次の行はあなたが思うようには見えecho bません。echo b 1余分な行があります1。なぜ?

さて、xargsに空白に分割するように指示します。改行文字を含むa b␤入力を渡しました。したがって、xargsは入力にa2つのフラグメントが含まれていることを確認しますb␤。指示に従って、xargsは最初に各フラグメントでbashを呼び出してecho a 1から実行しますecho b␤1

仕事を正しくする方法

findの一部のバージョンでは、シェルの断片をxargs含めることができます{}。これはほとんど常に悪い考えであり、一部のファイル名やその他のデータが破損し、通常はセキュリティホールになります。データを別々のパラメータとして渡します。

「find -exec sh -c」を使っても安全ですか?

答え2

〜のようにGilesは彼の答えで言及しました。-d ' 'GNU xargsのオプションを使用すると、スペースとスペースのみの区切り文字と見なされ、改行をデータの一部として残し、文字自体のようにシェルコードに含まれます。それはおそらくあなたが望むものではないでしょう。 (最も一般的な用途は、たとえば追加の処理なしで行をそのまま使用するように指示すること-dです。)-d '\n'-L

代わりに、スペースで区切られた各単語を別々のアイテムにする場合は、1つのオプションはスペースからアイテムを分割するデフォルトの動作を利用することで、次のように直接機能します。

$ echo a b | xargs -n1 bash -c 'echo "$1" 1' sh
a 1
b 1

引用符とバックスラッシュも処理するため(シェルとはわずかに異なる)、スペースを区切り文字として使用するのとは異なります。入力はa "b c"項目を生成しaますb c

または、withを使用して入力を前処理し、区切り文字として使用したいすべての文字を1文字に縮小することもでき-dますtr

$ printf 'a b\nc\n' | tr ' ' '\n' | xargs -d'\n' -n1 bash -c 'echo "$1" 1' sh
a 1
b 1
c 1

しかし、-dこれは標準ではなく、GNU xargsでのみ実装されると考えられています。したがって、代わりに-0、より幅広いサポートを提供するものを使用することをお勧めします。

$ printf 'a b\nc\n' | tr ' \n' '\0' | xargs -0 -n1 bash -c 'echo "$1" 1' sh
a 1
b 1
c 1

それにもかかわらず、シェルフラグメントに値を直接挿入しないでください。これは安全ではなく、任意の値で正しく動作することができないためです。

関連情報