前言:
由於需要獲得一個程式的已耗用時間,我們可以在Shell裡簡單地調用time command的形式來擷取一個程式啟動並執行real, user, system等時間資訊,但是預設輸出的形式類似:
real 0m0.077s
user 0m0.077s
sys 0m0.077s
假定我們只關心使用者態時間,那麼可以直接使用time command | grep user來擷取。但是再使用這個命令後,我們卻無法像往常一樣grep到我們想要的資訊。於是man time查看協助後,我們得知,原來time會預設將輸出寫到stderr中,那麼很簡單,我們可以time command 2>&1 | grep user,這樣將stderr重新導向到stdout,再進行grep就可以了。可以結果還是出乎我們的預料,依然無法grep。google一番之後才知道,這裡的time其實是一個shell的keyword,它的處理方式不同於普通的shell command。
NOTE: 可以通過 type 命令來查看一個command的類型,比如:
$ type [
[ is a shell builtin
$ type time
[ is a shell keyword
本文:
重新導向Shell關鍵字time
在一個Shell指令碼中,我想要獲得一個命令的執行時間,並把結果重新導向到一個檔案中。首先我嘗試如下命令:
$ time command > time.txt
不起作用。於是我發現time是輸出到stderr上的。我將命令改為:
$ time command 2> time.txt
還是沒用。time還是把結果列印到了console上。
顯然time是一個bash的保留字。它不像大多數內建的bash命令一樣,但卻是命令列文法(command line syntax)的一部分,就像 if 和 while。
Bash 3.1.0的手冊中提到了關於pipeline和保留字time的資訊:
Pipelines
A pipeline is a sequence of one or more commands separated by the char-
acter |. The format for a pipeline is:
[time [-p]] [ ! ] command [ | command2 ... ]
The standard output of command is connected via a pipe to the standard
input of command2. This connection is performed before any redirec-
tions specified by the command (see REDIRECTION below).
The return status of a pipeline is the exit status of the last command,
unless the pipefail option is enabled. If pipefail is enabled, the
pipeline's return status is the value of the last (rightmost) command
to exit with a non-zero status, or zero if all commands exit success-
fully. If the reserved word ! precedes a pipeline, the exit status of
that pipeline is the logical negation of the exit status as described
above. The shell waits for all commands in the pipeline to terminate
before returning a value.
If the time reserved word precedes a pipeline, the elapsed as well as
user and system time consumed by its execution are reported when the
pipeline terminates. The -p option changes the output format to that
specified by POSIX. The TIMEFORMAT variable may be set to a format
string that specifies how the timing information should be displayed;
see the description of TIMEFORMAT under Shell Variables below.
Each command in a pipeline is executed as a separate process (i.e., in
a subshell).
NOTE: 這裡我們看到,一個包含管道的命令,預設的傳回值將是最後一個子命令的傳回值。
內建的help命令提到:
$ help time
time: time [-p] PIPELINE
Execute PIPELINE and print a summary of the real time, user CPU time,
and system CPU time spent executing PIPELINE when it terminates.
The return status is the return status of PIPELINE. The `-p' option
prints the timing summary in a slightly different format. This uses
the value of the TIMEFORMAT variable as the output format.
times: times
Print the accumulated user and system times for processes run from
the shell.
這就暗示說,使用bash的關鍵字time,會使得命令運行在一個子shell中,即使沒有使用管道也是一樣。
我還沒有確認過這點,但是這確是為我們獲得命令的執行時間提供的一些線索。
從文法描述中我們可以清楚的看到,time的輸出不會被追加到命令的stderr中,而是由shell自身輸出的。
關鍵字time設定了一個標記,知道pipeline命令執行完,timing資訊才被列印到stderr中。time關鍵字要整個command和管道,還有相關的重新導向都要來得進階。這就是為什麼簡單的重新導向對於time而言不起作用。這是Bash文法定義的。command之後的重新導向對於time而言,是command的一部分。
重新導向time的輸出可以通過將整個time命令放入一個子shell中來實現:
$ (time command) 2> time.txt
啟動一個子shell不是必須的。我們同樣可以使用下面的代碼:
$ { time ls; } 2> time.txt
這可能比執行外部命令/usr/bin/time效率更高。當然time命令(外部命令)可能有你所需的更多的功能。
或許你想要依賴於bash time(關鍵字),因為你不知道系統安裝的time(外部命令)工具是否有你所需的功能。
在重新導向和管道行為這兩方面來看,使用上述命令同執行外部的time命令是等價的。
可以通過將stdout和stderr重新導向到/dev/null來抑制命令本身的輸出。
NOTE: 作者這裡也提到,可以使用外部命令來完成關鍵字time提供的功能。
原文: http://www.cs.tut.fi/~jarvi/tips/bash.html