findとxargsを丁寧に使わない

この記事はpaiza Advent Calendar 2022( https://adventar.org/calendars/8068 )の12/5の記事になります。
ジャンルフリーとのことでしたので、findとxargsの話をします。

はじめに

複数のファイルに対して同じような処理を適用したいときに、findとxargsを利用することが多いかと思います。
これらはとても便利なコマンドですが、いつまでたってもオプションの使い方を覚えることができません。
-nameの書き方わからん?-Pと-nと-Lって何が違うんだっけ?っとしょっちゅう年中なってます。

今回は難しいことはせずに、目の前の仕事をとにかく手っ取り早く終わることに焦点を当ててfindとxargsの最低限の使い方を確認します。

findにはオプションを与えない

findのオプションとしては-nameや-typeなどは便利でよく使うかと思いますが、オプションに与える引数のルールを覚えられませんし、オプションの頭のハイフン1つだったか2つだったかも忘れます。
ですので、findのオプションを使わないというのも1つの選択肢です。

# 指定したパス以下のファイルのパスをすべて列挙
$ find .

grepで絞り込む

findでファイルを絞りこまないならば、grepで絞り込みます。絞り込みたい文字列を書いておけば部分一致したもので絞り込んでくれるので、頭を使わなくても直感的に使えます。

# .inを含むファイルのみを列挙
$ find . | grep ".in"

もうちょっと複雑なことをしたい場合は-Eやegrepなどを使って正規表現を導入すると対応力が増します。

とにかくxargs -I{}と書く

xargsで複数回のコマンドを実行することができますが、引数をいくつずつ渡すとかコマンドのどこに挿入されるのかとか並列で処理されるのか、といったことが覚えられません。
とりあえず-I{}とすることで、受け渡されるパラーメータの名前を{}として使用できるようになり、その後ろは普段書いてるコマンドを{}を使って記述できます。
-Iの後ろの{}は別の文字列に置き換えが可能ですが、毎回考える時間が無駄なのでいつも同じ文字列を使うのがいいかなと思います。

# 列挙したファイルの名前をechoで1つずつ表示する
$ find . | grep ".in" | xargs -I{} echo {}

複雑なことをしたい場合はxargs -I{} bash -c

xargsで受け取った1つのパラメータを何度も使いたい場合や、リダイレクトやパイプなどを自然に使いたい場合がよくあります。
例えば、上で列挙した.inファイルを入力としてプログラムを何度も実行しつつ、入力のファイル名も表示して処理の経過や順序を確認したい場合などです。
bash -cでシェルの中にコマンドを並べてしまえば、難しいことを考えずに、いつものようにコマンドを書けます。

# 列挙した.inファイルのファイル名を表示しつつ、main.pyにリダイレクトで与える
$ find . | grep ".in" | xargs -I{} bash -c "echo {}; cat {} | python3 main.py"

おわりに

findとxargsの最低限の使い方を覚えておけば日常生活の中でループするような仕事をコンピュータに任せやすくなります。
また、小さな機能をパイプで繋げる方法だと、ちょっとした変更も容易で、例えば、上の方法だとファイル名の並びが汚いので、grepの後にパイプでsortを挟むといった改善をすることができます。
複雑なことや長期的に使うスクリプトで利用する場合は、パフォーマンスを考慮した対応や空白文字対応などより用途に応じたちゃんとした書き方を泣く泣く調べましょう。

この記事が気に入ったらサポートをしてみませんか?