与えられた入力の各行に対してxargsにコマンドを1回だけ実行させるにはどうすればよいですか?デフォルトの動作では、行をチャンクしてコマンドを1回実行し、各インスタンスに複数の行を渡します。
http://en.wikipedia.org/wiki/Xargs から:
/ path -type f -print0 | xargs -0 rm
この例では、findはxargsの入力にファイル名の長いリストを供給します。次に、xargsはこのリストをサブリストに分割し、サブリストごとに1回rmを呼び出します。これは、この機能的に同等のバージョンよりも効率的です。
find/path -type f -exec rm '{}' \;
Findには「exec」フラグがあることを知っています。別のリソースからの説明的な例を引用しています。
以下は、入力にスペースがない場合にのみ機能します。
xargs -L 1
xargs --max-lines=1 # synonym for the -L option
manページから:
-L max-lines
Use at most max-lines nonblank input lines per command line.
Trailing blanks cause an input line to be logically continued on
the next input line. Implies -x.
正しいとマークされたものを含め、このページにある既存の回答はすべて間違っているようです。これは、質問が曖昧に表現されているという事実に起因しています。
概要: commandコマンドを実行する場合 "指定された入力の各行に1回だけ、"行全体( 単一引数としてのコマンドへの改行なし)、これはそれを行うための最良のUNIX互換の方法です:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
には、tr
を廃止できる便利な拡張機能がある場合とない場合がありますが、OS Xや他のUNIXシステムでは使用できません。
長い説明のために…
Xargsを使用する際に考慮すべき2つの問題があります。
Xargsの動作をテストするには、実行されている回数と引数の数を示すユーティリティが必要です。そのための標準的なユーティリティがあるかどうかはわかりませんが、bashで簡単にコーディングできます。
#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
現在のディレクトリにshow
として保存し、実行可能にすると仮定すると、次のようになります。
$ ./show one two 'three and four'
-> "one" "two" "three and four"
さて、元の質問が本当に上記のポイント2に関するものである場合(私が思うに、何度か読んだ後)、それはこのように読まれるべきです(太字に変更):
xargsに、与えられた入力の引数ごとにコマンドを1回だけ実行させるにはどうすればよいですか?デフォルトの動作は、引数への入力をチャンクし、コマンド可能な限り数回を実行し、複数のargumentsを各インスタンスに渡します。
答えは-n 1
です。
Xargsのデフォルトの動作を比較してみましょう。デフォルトの動作では、入力を空白の周りに分割し、コマンドをできる限り数回呼び出します。
$ echo one two 'three and four' | xargs ./show
-> "one" "two" "three" "and" "four"
および-n 1
での動作:
$ echo one two 'three and four' | xargs -n 1 ./show
-> "one"
-> "two"
-> "three"
-> "and"
-> "four"
一方、元の質問がポイント1に関するものであり、入力の分割であり、次のように読まれた場合(ここに来る多くの人は、そうだと思うか、2つの問題を混同しているようです):
xargsにコマンドを実行させるにはどうすればいいですか?with正確に1つの引数各入力行に対してデフォルトの動作は、行を分割することですaround whitespace。
答えはもっと微妙です。
-L 1
が役立つと思う人もいるかもしれませんが、引数の解析は変わらないことがわかります。各入力行に対してコマンドを1回だけ実行し、その入力行にあった数の引数を使用します。
$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
それだけでなく、行が空白で終わる場合、次の行に追加されます。
$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
明らかに、-L
は、xargsが入力を引数に分割する方法を変更することではありません。
クロスプラットフォームの方法で(GNU拡張を除く)行う唯一の引数は-0
で、これは入力をNULバイトで分割します。
次に、tr
を使用して改行をNULに変換するだけです。
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show
-> "one " "two" "three and four"
これで、引数の解析は、末尾の空白も含めて問題なく見えるようになりました。
最後に、この手法と-n 1
を組み合わせると、入力が何であれ、入力行ごとに正確に1つのコマンドが実行されます。これは、元の質問(タイトルが与えられた場合、最も直感的)を見る別の方法かもしれません:
$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show
-> "one "
-> "two"
-> "three and four"
find
からのevery行(つまり、結果)に対してコマンドを実行する場合、xargs
には何が必要ですか?
試してください:
find
path-type f -exec
your-command{} \;
ここで、リテラル{}
はファイル名で置き換えられ、リテラル\;
は、find
がカスタムコマンドがそこで終了することを知るために必要です。
(質問の編集後、-exec
について知っていることを明確にした後)
man xargs
から:
-Lmax-lines
コマンドラインごとに最大でmax-lines空白以外の入力行を使用します。末尾の空白があると、入力行は論理的に次の入力行に続きます。 -xを意味します。
xargs
を使用すると、ブランクで終わるファイル名は問題を引き起こすことに注意してください。
$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\ b c\ c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory
したがって、-exec
オプションを気にしない場合は、-print0
と-0
を使用することをお勧めします。
$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a
与えられた入力の各行に対してxargsにコマンドを1回だけ実行させるにはどうすればよいですか?
-L 1
answerを使用しないのは、find -print0
の重要な機能であるスペースを含むファイルを処理しないためです。
echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: space.txt: No such file or directory
ls: with: No such file or directory
より良い解決策は、tr
を使用して改行をヌル(\0
)文字に変換してから、xargs -0
引数を使用することです。
echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt
その後、呼び出しの数を制限する必要がある場合は、-n 1
引数を使用して、入力ごとにプログラムを1回呼び出すことができます。
echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls
これにより、find beforeの出力をフィルター処理して、ブレークをヌルに変換することもできます。
find . -name \*.xml | grep -v /workspace/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar
別の選択肢...
find /path -type f | while read ln; do echo "processing $ln"; done
これら2つの方法も機能し、findを使用していない他のコマンドでも機能します。
xargs -I '{}' rm '{}'
xargs -i rm '{}'
ユースケースの例:
find . -name "*.pyc" | xargs -i rm '{}
pycファイルにスペースが含まれている場合でも、このディレクトリの下のすべてのpycファイルが削除されます。
find path -type f | xargs -L1 command
は、あなたが必要とすることすべてです。
次のコマンドは、/path
内のすべてのファイル(-type f)を検索し、cp
を使用して現在のフォルダーにコピーします。引数がファイル名の後に配置できるように、cp
コマンドラインでプレースホルダー文字を指定する場合は、-I %
を使用することに注意してください。
find /path -type f -print0 | xargs -0 -I % cp % .
Xargs(GNU findutils)4.4.0でテスト済み
--max-linesまたは--max-argsフラグをそれぞれ使用して、行数または引数(各引数の間にスペースがある場合)を制限できます。
-L max-lines Use at most max-lines nonblank input lines per command line. Trailing blanks cause an input line to be logically continued on the next input line. Implies -x. --max-lines[=max-lines], -l[max-lines] Synonym for the -L option. Unlike -L, the max-lines argument is optional. If max-args is not specified, it defaults to one. The -l option is deprecated since the POSIX standard specifies -L instead. --max-args=max-args, -n max-args Use at most max-args arguments per command line. Fewer than max-args arguments will be used if the size (see the -s option) is exceeded, unless the -x option is given, in which case xargs will exit.
@Draemonの回答は、ファイルにスペースがあっても「-0」で正しいようです。
Xargsコマンドを試していましたが、「-0」が「-L」と完全に機能することがわかりました。スペースも処理されます(入力がnullで終了した場合)。以下は例です:
#touch "file with space"
#touch "file1"
#touch "file2"
以下は、ヌルを分割し、リスト内の各引数でコマンドを実行します:
#find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2
したがって、-L1
は、「-0」と一緒に使用すると、ヌル終了文字ごとに引数を実行します。違いを確認するには:
#find . -name 'file*' -print0 | xargs -0 | xargs -L1
./file with space ./file1 ./file2
これでさえ一度実行されます:
#find . -name 'file*' -print0 | xargs -0 | xargs -0 -L1
./file with space ./file1 ./file2
「-L」がヌルバイトで分割されないため、コマンドは1回実行されます。動作するには「-0」と「-L」の両方を指定する必要があります。
上記のトビアの回答 にコメントを追加するのに十分な評判がないようですので、Windowsプラットフォームで同じようにxargs
を試してみたい人を助けるためにこの「回答」を追加しています。
Tobiaの迅速にコーディングされた「show」スクリプトと同じことを行うWindowsバッチファイルを次に示します。
@echo off
REM
REM cool trick of using "set" to echo without new line
REM (from: http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
exit /b
)
<nul set /p=Args: "%~1"
shift
:start
if not "%~1" == "" (
<nul set /p=, "%~1"
shift
goto start
)
echo.