ふと、ピザって10回表示するのにどういうbashのスクリプトでアプローチ出来るだろうか、と思い立って書いたしょうもない記事です。
for文
35文字。正攻法ですね。
# for i in {1..10};do echo pizza;done
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
while文
44文字。seq結果をプロセス置換(Process Substitution)でエセファイル化してます。
キモさは上がったけど文字数が増えた…。
# while read ln; do echo pizza;done< <(seq 10)
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
seq置換その1
24文字。seqで表示された数字をpizzaに置換する暴挙。
でも、パイプが気になる…。
# seq 10|sed 's/.*/pizza/'
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
seq置換その2
28文字。whileの時のプロセス置換とその1の合わせ技。
文字数は増えたけどパイプやセミコロンがなくなった!!綺麗!!
# sed 's/.*/pizza/'< <(seq 10)
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
番外: Python3の場合
38文字。end=""を入れないと最後が勝手に改行されて空行になってしまう…。
しかし文字列掛け算は強い。
# python -c 'print("pizza\n"*10,end="")'
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
番外: Rubyの場合
27文字。Pythonよりも文字数が少なくなった。
文字列掛け算には勝てない。
# ruby -e 'puts "pizza\n"*10'
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
pizza
番外の二つと公平を期すならbash -c使えよって感じですが、そこは標準シェルの特権ということで。
処理時間どうなの
ということで、いくら書きっぷりが良くても、CPUに優しいスクリプトじゃないとネェ…ということで、
1万回に回数あげて時間も調べました。
# time (for i in {1..10000};do echo pizza;done) 1>/dev/null
real 0m0.073s
user 0m0.069s
sys 0m0.004s
forはなかなかいいですね。
# time (while read ln; do echo pizza; done< <(seq 10000)) 1>/dev/null
real 0m0.150s
user 0m0.113s
sys 0m0.038s
while、テメーはだめだ。
# time (seq 10000|sed 's/.*/pizza/') 1>/dev/null
real 0m0.009s
user 0m0.009s
sys 0m0.002s
seq置換、はっや…。
# time (sed 's/.*/pizza/'< <(seq 10000)) 1>/dev/null
real 0m0.010s
user 0m0.007s
sys 0m0.002s
こちらもはやい。
# time (python -c 'print("pizza\n"*10000,end="")') 1>/dev/null
real 0m0.041s
user 0m0.034s
sys 0m0.007s
うん…。
# time (ruby -e 'puts "pizza\n"*10000') 1>/dev/null
real 0m0.045s
user 0m0.035s
sys 0m0.009s
ほぉ…。
なぜなのか
以下はstrace -cの結果です。
- for
# strace -c bash -c "for i in {1..10000};do echo pizza;done 1>/dev/null"
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
99.88 0.019789 2 10000 write
0.07 0.000013 0 33 brk
0.03 0.000005 3 2 dup2
0.01 0.000002 0 10 close
0.01 0.000002 0 5 fcntl
0.01 0.000001 0 8 fstat
0.01 0.000001 0 15 mmap
0.00 0.000000 0 4 read
0.00 0.000000 0 9 open
0.00 0.000000 0 14 5 stat
0.00 0.000000 0 8 mprotect
0.00 0.000000 0 2 munmap
0.00 0.000000 0 8 rt_sigaction
0.00 0.000000 0 6 rt_sigprocmask
0.00 0.000000 0 1 1 ioctl
0.00 0.000000 0 5 1 access
0.00 0.000000 0 1 getpid
0.00 0.000000 0 1 1 getpeername
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 uname
0.00 0.000000 0 1 getrlimit
0.00 0.000000 0 5 getuid
0.00 0.000000 0 5 getgid
0.00 0.000000 0 5 geteuid
0.00 0.000000 0 5 getegid
0.00 0.000000 0 1 getppid
0.00 0.000000 0 1 getpgrp
0.00 0.000000 0 1 arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00 0.019813 10158 8 total
個々のwrite処理の時間(usecs/call)は2μ秒という、かなり短い時間での処理ですが、
回数が10000回なので、結果としてかなり時間がかかってしまっています。
- seq sed
# strace -c bash -c "sed 's/.*/pizza/'< <(seq 10000) 1>/dev/null"
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
67.13 0.002038 1019 2 1 wait4
3.95 0.000120 5 24 8 stat
3.49 0.000106 13 8 open
3.46 0.000105 8 14 mmap
2.34 0.000071 9 8 mprotect
1.91 0.000058 7 8 close
1.88 0.000057 6 10 rt_sigaction
1.71 0.000052 13 4 read
1.71 0.000052 6 9 1 access
1.48 0.000045 45 1 clone
1.42 0.000043 4 12 rt_sigprocmask
1.32 0.000040 4 9 getuid
1.32 0.000040 4 9 getgid
1.28 0.000039 6 7 fstat
1.22 0.000037 4 9 geteuid
1.19 0.000036 4 9 getegid
0.96 0.000029 15 2 munmap
0.82 0.000025 5 5 brk
0.23 0.000007 7 1 uname
0.20 0.000006 6 1 rt_sigreturn
0.20 0.000006 6 1 getpid
0.20 0.000006 6 1 getrlimit
0.20 0.000006 6 1 getppid
0.20 0.000006 6 1 getpgrp
0.20 0.000006 6 1 arch_prctl
0.00 0.000000 0 1 1 getpeername
0.00 0.000000 0 1 execve
------ ----------- ----------- --------- --------- ----------------
100.00 0.003036 159 11 total
こちらは、一番重い処理は1msかかっているものの、2回しか呼ばれていないので、
トータルでは大した時間を消費していません。
当たり前といえば当たり前ですが、forやwhileではド真面目に1万回ループするので、
ループ回数に比例して処理時間が伸びてしまうんですね。おそらく。
従って、ループが少ない場合はむしろforが有利です。
# strace -c bash -c "for i in {1..100};do echo pizza;done 1>/dev/null" 2>&1|tail -1
100.00 0.001490 230 8 total
# strace -c bash -c "sed 's/.*/pizza/'< <(seq 100) 1>/dev/null" 2>&1|tail -1
100.00 0.002294 159 11 total
結論
適 材 適 所