こんにちは、山田ハヤオです。
プログラミングの入門のときに、よく「for文を使って1~10を表示しよう」みたいなのありますよね。
あれを何気なくシェルスクリプトでやってみようと思ったら案外いろんな方法がありました。
それをツイートしたのが以下
すると鍵垢で山Dが

と言ってきたのでベンチを回してみようと思います。
前提について
10000じゃつまらないので(?)1から100000までを出力させます。
処理の計測は処理を書いたシェルスクリプトをtime
コマンドで呼び出して計測します。
文字出力は全てBashの組み込みコマンドであるecho
を使用して行います。(printf
のほうが速いのかな?)
また、計算などの方法も、それぞれのループに合わせて最適なものを選んでいます。
(本当は処理は全て統一したほうが正しい結果が出るんでしょうけど)
/usr/bin/echo
は使いません(何が違うんでしょうかね…)
また、あくまでもシェルスクリプトなのでコンパイル言語は無しとします。
使用したBashのバージョンとPCのスペックを載せておきます。
$ LANG=C bash --version GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2019 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

forを使う方法
シェルスクリプトのforの基本構文をそのまま使います。
今回はbashのブレース展開と併用してnumber変数に1つづつ代入してechoしてもらいます。
改行がなければecho {1..100000}
#!/usr/bin/env bash
number=0
for number in {1..100000}; do
echo ${number}
done
whileを使う方法
while
はコマンドがtrueの間だけ処理を実行するコマンドです。
今回はtestコマンドと併用して「numberが10でない間」だけ文字を出力して1を足してもらいます。
testコマンドってtest
と[
と[[
ってありますけど速度に差はあるんでしょうか?
まぁ今回は一番良く使う[[
を使用します。
#!/usr/bin/env bash
number=0
while [[ ! ${number} == "100000" ]]; do
number=$(( number + 1))
echo ${number}
done
関数を使ったループ
自分が「何か普通じゃない方法でループできないだろうか」と考えた結果がこれです。
関数を再帰的に呼び出してループを行い、10に達したら関数を終了させます。
#!/usr/bin/env bash
number=0
count() {
if [[ "${number}" == "100000" ]]; then
return
else
number=$(( number + 1 ))
echo $number
fi
count
}
count
Pythonを使う
正直この方法は思いつきませんでした()
mk-linuxさんのコードをそのままコピペしてます。
#!/usr/bin/env bash
echo 1 100000 | python3 -c 'import sys; list = sys.stdin.readline().split(); print("\n".join([ str(i) for i in range(int(list[0]), int(list[1])+1)]))'
PHPを使う その1
この企画で初めて知ったんですがphp
コマンドって-r
を使うと直接コマンドラインからPHPを実行できるんですよね。それを利用した方法です。
PHPを使うという発想がまったく無かったのでstmkzaさんとnaokoさんに感謝です。
(ターミナルからシェルとして実行するためにいろいろ苦労しました)
#!/usr/bin/env bash
php -r '$a=$argv;$s=$a[1];for(;$s<=$a[2];$s++)echo "$s\n";' 1 100000
PHPを使う その2
全く別の処理らしいです。stmkzaさんありがとうございます。
#!/usr/bin/env bash
php -r 'echo implode("\n",range($argv[1],$argv[2]));' 1 100000
JavaScriptを使う
JavaScriptってコマンドラインで実行できるんですね…初めて知りました
kokkiemouseさんありがとうございましたm(_ _)m
#!/usr/bin/env bash
js68 -e 'for(let i=1;i < 100001;i++){ console.log(i);}'
seqコマンドを使う方法
もはやループじゃないです。もうみんなこれ使いましょうよ。絶対これ一番はy(ry
一応おまけとして測りましょうかね()
#!/usr/bin/env bash
seq 1 100000
バカ正直にechoする
もう何も考えないで頭悪くecho
します。
でも100000まで全部ちまちま書くほど暇じゃないのでコマンドでスクリプトを生成します(は?)
今回は準備なので下のコマンドの実行時間は含めません。あくまで生成されたスクリプトの時間です。
下のコマンドを考えるのが今回で一番むずかしかった部分です()
以下のコマンドはターミナルから直接実行します。
(echo -ne '#!/usr/bin/env bash\necho -e "'; for i in $(seq 1 100000); do if [[ "${i}" == "100000" ]]; then echo -n "${i}"; else echo -n "${i}\\n"; fi; done; echo "\"" ) > echo.sh
頭悪いスクリプトを作るために頭を使いました。
こんなツイートも見かけたのでありだと思います()
ソースコード
ソースコードはアーカイブファイルにまとめて置いておきます。ライセンスはWTFPLです。


https://山d.com/storage/misc/bash-loop.tar.xz
毎度のごとく山D氏のサーバを借ります()
MD5 d09e9548929800f4c6195751a1dc873d
SHA1 9cf9e6fe0a9811d844d15b77358e16b57769cf0a
検証タイム!
ソースコードの紹介が終わったので今から実際に測定をしようと思います。
測定はtime
コマンドを使用します。
forを使う
$ time ./for.sh 1 . 100000 real 0m0.748s user 0m0.526s sys 0m0.218s
whileを使う
$ time ./while.sh 1 . 100000 real 0m1.267s user 0m1.036s sys 0m0.225s
関数を使う
まさかの異常終了でした。何重にも呼びすぎて落ちたのかな…
1から100までテストした時はうまく言ったんです()
どうにか継続させる方法があったら教えて下さい。
$ time ./function.sh 1 . 8315 Segmentation fault (コアダンプ) real 0m9.008s user 0m8.381s sys 0m0.106s
Pythonを使う
$ time ./python.sh 1 . 100000 real 0m0.151s user 0m0.043s sys 0m0.052s
PHPを使う その1
$ time ./php1.sh 1 . 100000 real 0m0.420s user 0m0.081s sys 0m0.214s
PHPを使う その2
$ time ./php2.sh 1 . 100000 real 0m0.145s user 0m0.018s sys 0m0.059s
JavaScriptを使う
$ time ./js.sh 1 . 100000 real 0m0.453s user 0m0.133s sys 0m0.233s
seqを使う
$ time ./seq.sh 1 . 100000 real 0m0.142s user 0m0.009s sys 0m0.057s
echoする
$ time ./echo.sh 1 . 100000 real 0m0.359s user 0m0.116s sys 0m0.202s
ランキング付けをしよう
いろいろ考えるのは面倒なのでreal
でランキング付けをしてみます。
順位 | コマンド | 説明 | 時間 |
1 | seq.sh | seq 1 100000 | 0m0.142s |
2 | php2.sh | PHPその2 | 0m0.145s |
3 | python.sh | Pythonでforを使う | 0m0.151s |
4 | echo.sh | echoに全部書いた | 0m0.359s |
5 | php1.sh | PHP その1 | 0m0.420s |
6 | js.sh | JavaScriptでforを使う | 0m0.453s |
7 | for.sh | Bashでforを使う | 0m0.748s |
8 | while.sh | Bashでwhileを使う | 0m1.267s |
9 | function.sh | Bashで関数を使う(異常終了) | 0m9.008s |
seqが当然最速なのはまだしも、PHPがほぼ誤差レベルなのでかなり驚きました。
速いと予想してたJavaScriptが遅いのには少しショックです
一番苦労したechoはシェルスクリプトの中では好成績を残しました。(seqは入れないで)
やっぱりシェルスクリプトってくっそ遅いんだなぁって実感しました。
あとがき
シェルスクリプトのループを比較するつもりがいつのまにか他言語を使って処理してました()
PHPやPython、JSなどのコードを提供してくれた方々には本当に感謝です。
正直途中から何やってるのかわからなくなりました。何の意味があるんだこれ
まぁもしseqが使えなくてPythonやPHPが使える時はPHPでやると良いという結果ですね(やけくそ)
もし他のも「俺のコードのほうが速いぞ!」とか「こっちの言語のほうが速い!」という方はぜひコメントにお願いします。コンパイル言語は無しですよ!
コメント