初歩のシェルスクリプトで遊ぶ[ファイルのリネームツール、のようなもの?_(2)]
whileとforのパイプ繋ぎ
- for.sh
#!/bin/bash stdin=`cat -` for LINE in ${stdin} ; do expr ${LINE} + 1 #echo $(( ${LINE} + 1 )) done
- while.sh
#!/bin/bash while read LINE ; do expr ${LINE} + 1 #echo $(( ${LINE} + 1 )) done
$ time seq 0 1300 | ./for.sh 1>/dev/null real 0m1.022s user 0m0.868s sys 0m0.173s $ time seq 0 1300 | ./while.sh 1>/dev/null real 0m1.028s user 0m0.828s sys 0m0.221s $ time seq 0 1300 | ./for.sh | ./for.sh | ./for.sh | ./for.sh | ./for.sh 1>/dev/null real 0m5.203s user 0m4.089s sys 0m1.240s $ time seq 0 1300 | ./while.sh | ./while.sh | ./while.sh | ./while.sh | ./while.sh 1>/dev/null real 0m2.683s user 0m3.725s sys 0m1.395s
whileだと、1行をexprで計算するごとに、1行ずつ出力している。forでは、すべての数字を計算してから、まとめて出力している。
forでは単純に足し算、以上の時間がかかるが、whileでは同時処理をしてくれる。
という、簡単なテストをしてたんだが。
「(1)」に置いてるリネームツールは、forループのほうなわけっすよ。配列に入れたファイル名と拡張子を、コマンドひとつにつき一回ずつ呼び出して、配列に入れなおしている。同時処理はしていない。
もともと、ファイル一つにつきコマンドを一回呼び出す処理、オプション「--once」で、burdockは非常に遅い。これを今のスクリプトのまま改善したいなら、たとえばbashの内部コマンドだけで処理を書いてシェル関数にするとか、いっそbusyboxにするか、とかになるんだけれど。それは限度がある。
なら、まとめてパイプで処理できないかと。「--once」の処理を何度も繰り返すときなら、効果がありそうなんだが。
こんなん考えてるから、いつまでたっても安定せん。
今回は、bashの配列を使ってみよう、evalやIFS変更はなるべく避けよう、多少の遅さよりも読みやすさ優先、で始めたから、あんまりゴチャゴチャにしたくないんすよ。パイプに繋いで複数のコマンドを並列処理させるってなら、どうやって書けばいいだろかなぁ。パイプを繋いだコマンドの文字列を作って、evalで実行するくらいしか、やり方を思いつかん。
現実の道具としては、bash専用の自分用スクリプトなら、高速化のためにevalを乱用するより、読みやすさを重視したほうがいいと思う。
bashに限定せずに他のシェルでも動くようにするのなら、多少の読みにくさ、トラブルの起きやすさは、しょうがない。しょうがないんだけど……readコマンドがbashのでしか使い物にならないから、やっぱりforにするしかない。パイプで繋いでいても、スクリプトの中身がforなら、並列動作になんないんだから、パイプにする必要もないんじゃなかろうか。
書くなら、dashとbusyboxで使えるバージョンのほうかなぁ。