time と /usr/bin/time

Ubuntu 環境での話。Debian も同様のはずだけど、その他の Linux では違うかもしれない。

困った事

シェル組み込み time*1と /usr/bin/time があって、time とだけ入力するとコマンドライン*2は前者、シェルスクリプト*3だと後者が呼ばれて、その表示内容が異なる。

$ time sleep 1

real	0m1.002s
user	0m0.000s
sys	0m0.000s
$ echo time sleep 1 > tmp.sh  ; sh tmp.sh *4
0.00user 0.00system 0:01.00elapsed 0%CPU (0avgtext+0avgdata 572maxresident)k
0inputs+0outputs (0major+188minor)pagefaults 0swaps

また、man は /usr/bin/time の記述なので、man time を見て

time -f "\t%E real,\t%U user,\t%S sys" 〜

の記述をみて同じのをコマンドラインで試しても、-f をコマンド名と解釈されてエラーが出る。

$ time -f "\t%E real,\t%U user,\t%S sys" sleep 1
-f: コマンドが見つかりません

real	0m0.083s
user	0m0.056s
sys	0m0.024s

そもそもの話

単に sh や dash に組み込みコマンド time が無いだけで、bashコマンドラインの time と sh スクリプトの time が違かっただけ。

互換オプション -p

/usr/bin/time には組み込みtimeとの互換オプションがあって、/usr/bin/time -p 〜 とすれば、time 〜 と同じ表示が出る。なので /usr/bin/time に統一すれば困らない(?)

$ /usr/bin/time sleep 1
0.00user 0.00system 0:01.00elapsed 0%CPU (0avgtext+0avgdata 572maxresident)k
0inputs+0outputs (0major+189minor)pagefaults 0swaps
$ /usr/bin/time -p sleep 1
real 1.00
user 0.00
sys 0.00

bash 組み込み time コマンドでも -p を受け付けるので、絶対パスで書かなくても time -p というふうに常にオプションを付けておけば安心。(ozuma さんコメントありがとうございます!)

for

と思ったら、やっぱり困る事はあって。bash組み込みのtimeであれば、

time for 〜

で、for の中の時間を丸ごと測れるが、スクリプト中の time は /usr/bin/time を呼ぶので、

/usr/bin/time for 〜

だと動かない。スクリプトの先頭に

#! /bin/bash

を付けて、bash のある環境前提で考えるのが楽だと思います。

*1:この組み込みが sh か bash かを考えていれば、もうちょい早く解決した事なんだけど。。

*2:bash を動かしてるという事にします

*3:bash でなく sh や dash

*4:echo time sleep 1 | sh - でも良いけど、分かりにくいのでベタに書いてます