[シェル芸]for文を使わず一撃でサブディレクトリを多数作成する
あけましておめでとうございます。本年もどうぞよろしくお願いいたします。親戚付き合いのない私にとって、良くも悪くも正月は暇なので、シェル芸とサーバーリプレースごっこで遊んでいます。
※ 2015-01-03 00:19:38 追記
さいとうさんよりツッコミありました。桁揃えのブレース展開ができるとは知らなかった・・・sh行不足でした(;´д`)
@nullpopopo echo {1..2}/{00001..10000} | xargs -P 0 mkdir -p
じゃだめ?
0.1 秒以内で終了するが・・・。http://t.co/dTMTdt3heY— Hirofumi Saito (@hi_saito) January 2, 2015
今回、以下のような構造のディレクトリを作成しようと思い、シェル芸を考えてみました。もしかしたら過去のシェル芸勉強会で似たような(もしくは全く同じ)問題があったかも知れませんが、気にしないことにします。お正月ですから。
1/00001 1/00002 ... 1/10000 2/00001 2/00002 ... 2/10000
まずはシェルのfor文を使わないパターンで、timeコマンドをつけて時間を計測しながらやってみましょう。まずは「1」ディレクトリと「2」ディレクトリの下に00001〜10000のサブディレクトリを作ってみます。
$ A=$(echo \{$(seq -w 1 10000)\} | sed -e 's/ /,/g') ; echo "mkdir -p 1/$A 2/$A" | time sh 0.13user 0.37system 0:00.60elapsed 84%CPU (0avgtext+0avgdata 7356maxresident)k 0inputs+0outputs (0major+2013minor)pagefaults 0swaps
timeコマンドの表示が横1列になってしまいますが、0.13秒で処理が終わりました。次はシェルのfor文を使って同じことをやってみます。
$ time for A in $(seq -w 1 10000); do mkdir -p {1,2}/$A; done real 0m14.708s user 0m2.806s sys 0m12.280s
こちらは0.14秒とあまりパフォーマンスは変わりません。それでは次に100000個のサブディレクトリを作ってみましょう。最初にfor文を使わないパターンです。
$ A=$(echo \{$(seq -w 1 100000)\} | sed -e 's/ /,/g') ; echo "mkdir -p 1/$A 2/$A" | time sh 6.72user 5.04system 0:16.63elapsed 70%CPU (0avgtext+0avgdata 47956maxresident)k 0inputs+0outputs (0major+18260minor)pagefaults 0swaps
7秒弱でできました。それでは同じくfor文でやってみましょう。
$ time for A in $(seq -w 1 100000); do mkdir -p {1,2}/$A; done real 2m30.545s user 0m27.905s sys 2m4.039s
なんと2分30秒もかかっています!これは由々しき事態です。
さて、シェル芸を使って一撃で複数のサブディレクトリを作るにあたって、for文を使えば簡単にできる(人が実行したワンライナーが理解しやすい)というのは大多数が直感的に思うでしょう。しかし、パフォーマンスを犠牲にせず、かつワンライナーでエレガントに終わらせたい!という場合にどうするかを考えた結果、ブレース展開の文字列を作って変数に格納してあげることにしました。
また、↑の例ではわざわざmkdirのコマンドをechoの文字列としていますが、多数のディレクトリを作成するにはこれが安全じゃないかと。最後のパイプラインを外して実行結果を確認すれば、間違って変なディレクトリができてしまうことも防げます。例えばこんな感じです。
$ A=$(echo \{$(seq -w 1 5)\} | sed -e 's/ /,/g') ; echo "mkdir -p 1/$A 2/$A" mkdir -p 1/{1,2,3,4,5} 2/{1,2,3,4,5}
こうすることで実行前の確認ができるので便利ですね。
今年もよいシェル芸ライフを送りましょう!でわ〜♪
[amazonjs asin=”4904807154″ locale=”JP” title=”シェルスクリプトマガジン vol.21″]