Is there a command like time
that can display the running time details of the last or past executed commands on the shell?
views:
129answers:
3Edit 3:
The structure of this answer:
- There is no ready-made way to time a command that has already been run
- There are ways that you can deduce a guesstimate of the duration of a command's run time.
- A proof of concept is shown (starting with the hypothesis that it can't be done and ending with the conclusion that the hypothesis was wrong).
- There are hacks you can put into place before-hand that will record the elapsed time of every command you run
- Conclusion
The answer labeled by its parts according to the outline above:
Part 1 - the short answer is "no"
Original
Nope, sorry. You have to use time
.
Part 2 - maybe you can deduce a result
In some cases if a program writes output files or information in log files, you might be able to deduce running time, but that would be program-specific and only a guesstimate. If you have HISTTIMEFORMAT set in Bash, you can look at entries in the history file to get an idea of when a program started. But the ending time isn't logged, so you could only deduce a duration if another program was started immediately after the one you're interested in finished.
Part 3 - a hypothesis is falsified
Hypothesis: Idle time will be counted in the elapsed time
Edit:
Here is an example to illustrate my point. It's based on the suggestion by ZyX, but would be similar using other methods.
In zsh
:
% precmd() { prevstart=start; start=$SECONDS; }
% preexec() { prevend=$end; end=$SECONDS; }
% echo "T: $SECONDS ps: $prevstart pe: $prevend s: $start e: $end"
T: 1491 ps: 1456 pe: 1458 s: 1459 e: 1491
Now we wait... let's say for 15 seconds, then:
% echo "T: $SECONDS"; sleep 10
T: 1506
Now we wait... let's say for 20 seconds, then:
% echo "T: $SECONDS ps: $prevstart pe: $prevend s: $start e: $end"
T: 1536 ps: 1492 pe: 1506 s: 1516 e: 1536
As you can see, I was wrong. The start time (1516) minus the previous end time (1506) is the duration of the command (sleep 10
). Which also shows that the variables I used in the functions need better names.
Hypothesis falsified - it is possible to get the correct elapsed time without including the idle time
Part 4 - a hack to record the elapsed time of every command
Edit 2:
Here are the Bash equivalents to the functions in ZyX's answer (they require the script linked to there):
preexec () {
(( ${#_elapsed[@]} > 1000 )) && _elapsed=(${_elapsed[@]: -1000})
_start=$SECONDS
}
precmd () {
(( _start >= 0 )) && _elapsed+=($(( SECONDS-_start )))
_start=-1
}
After installing preexec.bash
(from the linked script) and creating the two functions above, the example run would look like this:
$ _elapsed=() # Clear the times
$ sleep 10s
$ sleep 2s ; echo ${_elapsed[@]: -1}
10
$ echo ${_elapsed[@]}
0 10 2
Part 5 - conclusion
Use time
.
I think you can only get timing statistics for commands you run using the command 'time'.
From the man page:
time [options] command [arguments...]
DESCRIPTION The time command runs the specified program command with the given arguments. When command finishes, time writes a message to standard error giving timing statistics about this program run.
I do not know, how it is in bash, but in zsh you can define preexec
and precmd
functions so that they save the current time to variables $STARTTIME
(preexec) and $ENDTIME
(precmd) so you will be able to find the approximate running time. Or you can define an accept-line
function so that it will prepend time
before each command.
UPDATE:
This is the code, which will store elapsed times in the $_elapsed
array:
preexec () {
(( $#_elapsed > 1000 )) && set -A _elapsed $_elapsed[-1000,-1]
typeset -ig _start=SECONDS
}
precmd() { set -A _elapsed $_elapsed $(( SECONDS-_start )) }
Then if you run sleep 10s
:
% set -A _elapsed # Clear the times
% sleep 10s
% sleep 2s ; echo $_elapsed[-1]
10
% echo $_elapsed
0 10 0
No need in four variables. No problems with names or additional delays. Just note that $_elapsed
array may grow very big, so you need to delete the first items (this is done with the following piece of code: (( $#_elapsed > 1000 )) && set -A _elapsed $_elapsed[-1000,-1]
).
UPDATE2:
Found the script to support zsh-style precmd and preexec in bash. Maybe you will need to remove typeset -ig
(I used just to force $_start
to be integer) and replace set -A var ...
with var=( ... )
in order to get this working. And I do not know how to slice arrays and get their length in bash.
Script: http://www.twistedmatrix.com/users/glyph/preexec.bash.txt
UPDATE3:
Found one problem: if you hit return with an empty line preexec does not run, while precmd does, so you will get meaningless values in $_elapsed
array. In order to fix this replace the precmd
function with the following code:
precmd () {
(( _start >= 0 )) && set -A _elapsed $_elapsed $(( SECONDS-_start ))
_start=-1
}