日々の業務で利用しているコマンドの中で、xargs コマンドはちょっと面倒な処理をワンライナーで書けるようになるとても便利なコマンドです。この記事では xargs コマンドの使い方について紹介します。
フロントエンドとバックエンドの開発をいったりきたりしています。
趣味は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 はとても便利で、ログ調査やバッチ処理などにも使える優れものです。
業務のお役に立てれば幸いです。