开发者

Is there a way to find the running time of the last executed command in the shell?

Is there a command like time that can display the running time details of the last or pa开发者_运维知识库st executed commands on the shell?


zsh has some built in features to time how long commands take.

If you enable the inc_append_history_time option with

setopt inc_append_history_time

Then the time taken to run every command is saved in your history and then can be viewed with history -D.


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 (web.archive)

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 
}


Edit 3:

The structure of this answer:

  1. There is no ready-made way to time a command that has already been run
  2. There are ways that you can deduce a guesstimate of the duration of a command's run time.
  3. 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).
  4. There are hacks you can put into place before-hand that will record the elapsed time of every command you run
  5. 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 take it you are running commands that take a long time and not realizing at the beginning that you would like to time how long they take to complete. In zsh if you set the environment variable REPORTTIME to a number, any command taking longer than that number of seconds will have the time it took printed as if you had run it with the time command in front of it. You can set it in your .zshrc so that long running commands will always have their time printed. Note that time spent sleeping (as opposed to user/system time) is not counted towards triggering the timer but is still tracked.


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 wrote a tool (discalimer!) which tracks the command-start- and stop-time, besides other command meta-data (e.g. cwd or modified files) by hijacking into a running shell-process (currently only bash is supported). In contrast to the other solutions posted here, it also correctly tracks the run-time of async commands, e.g. sleep 5 &.
Check it out here:

https://github.com/tycho-kirchner/shournal


One can use the history command with epoch seconds %s and the built-in variable $EPOCHSECONDS to calculate when the command finished by leveraging only $PROMPT_COMMAND.

# Save start time before executing command (does not work due to PS0 sub-shell)
# preexec() {
#   STARTTIME=$EPOCHSECONDS
# }
# PS0=preexec

# Save end time, without duplicating commands when pressing Enter on an empty line
precmd() {
    local st=$(HISTTIMEFORMAT='%s ' history 1 | awk '{print $2}');
    if [[ -z "$STARTTIME" || (-n "$STARTTIME" && "$STARTTIME" -ne "$st") ]]; then
        ENDTIME=$EPOCHSECONDS
        STARTTIME=$st
    else
        ENDTIME=0
    fi
}

__timeit() {
    precmd;
    if ((ENDTIME - STARTTIME >= 0)); then
        printf 'Command took %d seconds.\n' "$((ENDTIME - STARTTIME))";
    fi

    # Do not forget your:
    #     - OSC 0 (set title)
    #     - OSC 777 (notification in gnome-terminal, urxvt; note, this one has preexec and precmd as OSC 777 features)
    #     - OSC 99 (notification in kitty)
    #     - OSC 7 (set url) - out of scope for this question
}

export PROMPT_COMMAND=__timeit

Note: If you have ignoredups in your $HISTCONTROL, then this will not report back for a command that is re-run.

Reference (copied from my own answer to a similar question):

Use PS0 and PS1 to display execution time of each bash command

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜