web-dev-qa-db-ja.com

入力の各行に対してxargsにコマンドを1回実行させます

与えられた入力の各行に対して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」フラグがあることを知っています。別のリソースからの説明的な例を引用しています。

295
Readonly

以下は、入力にスペースがない場合にのみ機能します。

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.
357
Draemon

正しいとマークされたものを含め、このページにある既存の回答はすべて間違っているようです。これは、質問が曖昧に表現されているという事実に起因しています。

概要: commandコマンドを実行する場合 "指定された入力の各行に1回だけ、"行全体( 単一引数としてのコマンドへの改行なし)、これはそれを行うための最良のUNIX互換の方法です:

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargsには、trを廃止できる便利な拡張機能がある場合とない場合がありますが、OS Xや他のUNIXシステムでは使用できません。

長い説明のために…


Xargsを使用する際に考慮すべき2つの問題があります。

  1. 入力をどのように「引数」に分割しますか。そして
  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" 
165
Tobia

findからのevery行(つまり、結果)に対してコマンドを実行する場合、xargsには何が必要ですか?

試してください:

findpath-type f -execyour-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
22
tzot

与えられた入力の各行に対して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
14
Gray

別の選択肢...

find /path -type f | while read ln; do echo "processing $ln"; done
10
Richard

これら2つの方法も機能し、findを使用していない他のコマンドでも機能します。

xargs -I '{}' rm '{}'
xargs -i rm '{}'

ユースケースの例:

find . -name "*.pyc" | xargs -i rm '{}

pycファイルにスペースが含まれている場合でも、このディレクトリの下のすべてのpycファイルが削除されます。

4
Alex Riedler
find path -type f | xargs -L1 command 

は、あなたが必要とすることすべてです。

4
Nic Ferrier

次のコマンドは、/path内のすべてのファイル(-type f)を検索し、cpを使用して現在のフォルダーにコピーします。引数がファイル名の後に配置できるように、cpコマンドラインでプレースホルダー文字を指定する場合は、-I %を使用することに注意してください。

find /path -type f -print0 | xargs -0 -I % cp % .

Xargs(GNU findutils)4.4.0でテスト済み

3
Gerry Shaw

--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.
1
Readonly

@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」の両方を指定する必要があります。

0
Mohammad Karmi

上記のトビアの回答 にコメントを追加するのに十分な評判がないようですので、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.
0
CrashNeb