LoginSignup
8
5

More than 5 years have passed since last update.

ピザって10回表示するbashワンライナーを考える

Last updated at Posted at 2017-10-11

ふと、ピザって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

結論

適 材 適 所

8
5
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5