xargsのクセにうんざり!爆速で処理できる自作Bash列挙ツールを作ってみた
I wrote an bash enumerator because I was sick of xargs
I wrote an bash enumerator because I was sick of xargs
xargsの挙動に毎度イライラしていたので、自分専用のBash列挙ツールを書いてみました。これで面倒なパイプライン処理ともおさらばです。
find -print0 | xargs -0 -I {} "the {} iterated command"
これの何が良いのかよく分からない。単一ツールで大量のオプションを覚えることは、複数のツールや構文を使い分けることより優れているのか?特に後者は他の場所でも役立つし。それにスクリプトと一緒に配布しない限り使えないとなると……うーん、微妙だね。
シンプルにこれでいいでしょ。
foo | while read X; do bar "$X"; done
xargsの代替という話題なら、自分はgnu parallelが好きだな。
--dry-runフラグがあるおかげで、xargsを使っていた頃より安心してバッチ処理ができるようになった。
parallelはあらゆることに対するオプションが用意されていて、多すぎて困るくらいだけど。
まぁ、他の代替ツールも調べてはみたよ。
作者のOle Tangeが、代替ツールとその違いについてめちゃくちゃ詳細な記事をメンテナンスしている。[0]
gnu parallelの本や読み物 [1] も素晴らしいしね。
[0] https://www.gnu.org/software/parallel/parallel_alternatives.html
[1] https://www.gnu.org/software/parallel/#Tutorial
zshならこんな感じで書けるよ:
for x (*.sh); echo "before $x after"
または
for x in *.sh; echo "before $x after"
または
for x (*.sh) { echo -n "before "; echo -n $x; echo " after" }
普通にxargsの-I {}とクォーテーションマークを使うだけでいいのでは?
もっとシンプルなもの、つまり何でも反復処理できる一貫した方法が欲しかった
でも、実際にはそれはシンプルじゃないよ。シンプルっていうのは不要なものをすべて削ぎ落とすこと。やりたいことをすでに実現できるコマンドがあるのに、わざわざ特定のやり方でコマンドを呼び出すプログラムを追加しているだけだよね。これだとバグやメンテナンスの悩みの種が増えるし、ポータビリティも損なわれる。これは複雑さを増長させているだけだよ。
あなたがこのスクリプトを作った理由は「シンプルさ」を求めたからじゃなくて、「手軽さ」を求めたからだろうね。それは別に悪いことじゃないし、特にこうしたコマンドに慣れていない人にとっては確かに楽だろう。でも「手軽」と「シンプル」はイコールじゃない。「シンプルであることは難しいが、手軽であることは複雑さを先送りにしているだけだ」と気づくことは多いよ。
自分は、コマンドを外部で構築してからgnu parallelに渡すのが一番確実で好きだな(主に--etaや--tmuxpaneのために)。あと他の人が言ってるように、xargs -Iも素晴らしい。自分は短くて衝突しにくい'@'を使うのが好み。
seq 1 10|xargs -I@ echo 'bash run.py @'|parallel -j 10
echoを使うのはちょっと間抜けに見えるかもしれないけど、こうすれば|parallelを外して、コマンドが正しいか確認できるからね。それに並列処理がいらなければ、bashに渡すだけだし。
gnu parallelの特定のオプションセットに対するエイリアスで十分な気がするな。
自分はこれを使ってる:
find . -name '*.log' -print0 | xargs -0 rm
今回の(記事にあるような)単純な例なら、findコマンド自体にdelete演算子もあるよ。
このnull終端処理は今やPOSIX標準だしね。