TECHSCORE BLOG

クラウドCRMを提供するシナジーマーケティングのエンジニアブログです。

xargsでお手軽分割処理

日々の業務で利用しているコマンドの中で、xargs コマンドはちょっと面倒な処理をワンライナーで書けるようになるとても便利なコマンドです。この記事では xargs コマンドの使い方について紹介します。

高橋 正博(タカハシ マサヒロ)
2017年4月入社
フロントエンドとバックエンドの開発をいったりきたりしています。
趣味はFinalFantasyXIVです。


前提

GNU版xargs(gxargs)を利用しています。

あちこちに散らばったファイルをまとめて処理したいことってありませんか?

そんな時、xargs があれば以下のような事ができます。

例えば、以下のようなディレクトリ状況の時に

# ディレクトリ状況
$ tree
.
├── 111
│   └── 111.txt
├── 222
│   └── 222.txt
├── 333
│   └── 333.txt
├── aaa.csv
├── aaa.txt
├── bbb.txt
└── ccc.txt

3 directories, 7 files
# 各ファイルの中身
$ head *.*
==> aaa.csv <==
a1,a2,a3
a11,a21,a31
a12,a22,a32

==> aaa.txt <==
a
aa
aaa
aaaa
aaaaa

==> bbb.txt <==
b
bb
bbb
bbbb
bbbbb

==> ccc.txt <==
c
cc
ccc
cccc
ccccc

$ head */*
==> 111/111.txt <==
111

==> 222/222.txt <==
222

==> 333/333.txt <==
333

ファイルごとに先頭から3行だけ出力することができます。

# ファイルごとの先頭3行出力
$ find . -name "*.txt" | sort | xargs -L 1 head -3
111
222
333
a
aa
aaa
b
bb
bbb
c
cc
ccc

標準入力で動作するので、色々な事に使えそうですよね。
デフォルトでホワイトスペース区切りで分割処理をするので、空白でも改行でも機能します。

また、-L 1 オプションを使用していますが、これによりホワイトスペース1つ毎に分割して引数に渡すことができます。
※詳細は -L max-linesの使用例 をご参照ください。

xargs は、個人的には以下のシチュエーションの時によく使います。

  • ls、find、grep などで見つけたファイルを処理したい時
  • CSV などの複数行ファイルを各行処理したい時

基本的な動作解説

xargs は標準入力で受け取った文字列を分割して、引数で指定されたコマンドの後ろに渡して実行します。

引数に head コマンドを渡すと、

# 引数に head コマンドを渡した場合
$ find . -name "*.txt" | sort | xargs -L 1 head -3
111
222
333
a
aa
aaa
b
bb
bbb
c
cc
ccc

以下のコマンドを実行するのと同等の結果となります。

# 上記のコマンドと同等
$ head -3 ./111/111.txt
$ head -3 ./222/222.txt
$ head -3 ./333/333.txt
$ head -3 ./aaa.txt
$ head -3 ./bbb.txt
$ head -3 ./ccc.txt

日々の業務で利用しているコマンドの中で、xargs コマンドはちょっと面倒な処理をワンライナーで書けるようになるとても便利なコマンドです。

オプション使用例

xargs にもオプションが用意されています。

その中でも、個人的には以下の2つのオプションをよく使います。

オプション 意味
-L max-lines 1 コマンド行につき最大 max-lines 行の (空行ではない) 入力行を使用します。
入力行の行末に空白文字が付いていると、 その行は次の入力行に論理的に続いていることになります。
-x の指定を暗に含みます。
-0, --null 入力される項目が、 ホワイトスペース (空白や改行) ではなく、 ヌル文字によって区切られます。
また、 クォートやバックスラッシュは、 特別な扱いをしません (つまり、 すべての文字がそのままに解釈されます)。
ファイル終端文字列は無効になり、 他の引数と同じように扱われます。
このオプションは、 入力される項目にホワイトスペース、 クォート、 バックスラッシュなどが含まれる可能性がある場合に、 役に立ちます。
GNU find の -print0 オプションは、 このモードに適した入力を生成します。

-L max-linesの使用例

ファイルごとに先頭から3行だけ出力したいときに、オプション未使用だと、
以下のように head -3 ./111/111.txt ./222/222.txt ./333/333.txt ./aaa.txt ./bbb.txt ./ccc.txt と同じ結果になってしまいますが、

# オプション未使用
$ find . -name "*.txt" | sort | xargs head -3
==> ./111/111.txt <==
111

==> ./222/222.txt <==
222

==> ./333/333.txt <==
333

==> ./aaa.txt <==
a
aa
aaa

==> ./bbb.txt <==
b
bb
bbb

==> ./ccc.txt <==
c
cc
ccc

以下のように -L 1 オプションを使用することで、ファイルごとに処理できます。

# オプション使用
$ find . -name "*.txt" | sort | xargs -L 1 head -3
111
222
333
a
aa
aaa
b
bb
bbb
c
cc
ccc

もう少し解説をすると、オプション無しだと以下のように一括で標準出力されていて、

# オプション未使用
$ find . -name "*.txt" | sort | xargs
./111/111.txt ./222/222.txt ./333/333.txt ./aaa.txt ./bbb.txt ./ccc.txt

以下のように -L 1 オプション付きだと、ホワイトスペース1つ毎に区切られて標準出力されるようになります。

# -L 1 オプション使用
$ find . -name "*.txt" | sort | xargs -L 1
./111/111.txt
./222/222.txt
./333/333.txt
./aaa.txt
./bbb.txt
./ccc.txt

以下のように -L 2 オプション付きだと、ホワイトスペース2つ毎に区切られて標準出力されるようになります。

# -L 2 オプション使用
$ find . -name "*.txt" | sort | xargs -L 2
./111/111.txt ./222/222.txt
./333/333.txt ./aaa.txt
./bbb.txt ./ccc.txt

以下のように -L 2 オプション付きの結果を、$#を出力するだけの shell スクリプトに渡すと、引数が2つずつになっているのが分かります。

# 引数が2つであることを確認
$ cat test.sh
echo $#

$ find . -name "*.txt" | sort | xargs -L 2 ./test.sh
2
2
2

-0,--nullの使用例

ファイル名にスペースを含んでいても、区切り文字をヌル文字にする事で処理ができます。

# ファイル名にスペースを含むtxtファイルを作成
$ echo -e "d\ndd\nddd\ndddd\nddddd" > "ddd ddd".txt
$ tree
.
├── 111
│   └── 111.txt
├── 222
│   └── 222.txt
├── 333
│   └── 333.txt
├── aaa.csv
├── aaa.txt
├── bbb.txt
├── ccc.txt
└── ddd\ ddd.txt

3 directories, 8 files

txt ファイルを find して、それぞれ上3行出力したいときに、
ファイル名にスペースが含まれているとエラーになりますが、

# ファイル名にスペースが含まれているとエラー
$ find . -name "*.txt" | xargs -L 1 head -3 | sort
head: ./ddd: No such file or directory
head: ddd.txt: No such file or directory
111
222
333
a
aa
aaa
b
bb
bbb
c
cc
ccc

xargs の -0 オプションで、ヌル文字区切りで処理をするようにして、
それに合わせて find の -print0 オプションでヌル文字区切りで出力することで、正常に処理ができるようになります。

# ヌル文字区切りで処理
$ find . -name "*.txt" -print0 | xargs -0 -L 1 head -3 | sort
111
222
333
a
aa
aaa
b
bb
bbb
c
cc
ccc
d
dd
ddd

最近使った例

私が最近 xargs を使った実例を紹介します。

例1:git でコンフリクト解消する時に、UU ステータスになっていたものをまとめて git add する。

$ git status -s | grep "UU" | awk '{print $2}' | xargs git add

例2:cron で更新から60日以上経過しているディレクトリを削除する。

30 10 * * * /bin/find /trash_dirs/ -maxdepth 1 -type d -mtime +60 | xargs rm -rf

補足

GNU版(Linuxなど)とBSD版(Macなど)で挙動やオプションが異なるので注意が必要です。
xargs を Linux サーバーで活用する事が多いと思いますので、個人的には Mac で利用する場合であっても GNU版を使うことをおすすめします。

# MacでGNU版xargs導入例
$ brew install findutils

# お好みでエイリアス設定を(.bash_profile などに設定)
$ alias xargs="gxargs"

おわりに

xargs はとても便利で、ログ調査やバッチ処理などにも使える優れものです。
業務のお役に立てれば幸いです。

シナジーマーケティング株式会社では一緒に働く仲間を募集しています。