Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Elapsed and execution time for commands in ZSH

Elapsed and execution time display for commands in ZSH

Append this to your ~/.zshrc file.

function preexec() {
  timer=$(($(date +%s%0N)/1000000))
}

function precmd() {
  if [ $timer ]; then
    now=$(($(date +%s%0N)/1000000))
    elapsed=$(($now-$timer))

    export RPROMPT="%F{cyan}${elapsed}ms %{$reset_color%}"
    unset timer
  fi
}

Remixed from @adri's snippet.

@kimonoki

This comment has been minimized.

Copy link

@kimonoki kimonoki commented May 18, 2019

I get preexec:1: bad math expression: operator expected at `N/1000000'

@jep-dev

This comment has been minimized.

Copy link

@jep-dev jep-dev commented Jul 13, 2019

Just thought I'd post my 2 cents. I think you want date +%s%0N over date+%s%N, unless I'm missing something; otherwise nanoseconds are printed by %N without leading zeroes, independently of the %s. If you're using nanos as fractional seconds (aka fixed point arithmetic with ns as the atomic unit) the leading zeroes become significant. So for example, 1s+108ns = "1100000000", 1s+107ns = "110000000", ..., 1s+1ns = "11". (You want 1s+108ns = "1100000000", 1s+107ns = "1010000000", ..., 1s+1ns = "1000000001".) You can expect 10% of measurements to have at least 1 leading zero in the nanos, and each omitted zero to cause an order of magnitude of error in the total time. Hope this helps!

@knadh

This comment has been minimized.

Copy link
Owner Author

@knadh knadh commented Jul 14, 2019

@jep-dev You're right. Updated the gist. Thank you.

@knadh

This comment has been minimized.

Copy link
Owner Author

@knadh knadh commented Jul 14, 2019

@kimonoki Sorry, missed your comment. Unsure about the error.

@rufreakde

This comment has been minimized.

Copy link

@rufreakde rufreakde commented Aug 13, 2019

@kimonoki Getting the same error:
bad math expression: operator expected at 'N/1000000'

timer=$(($(date +%s%0N)/1000000)) - error
$(($(date +%s)/1000000)) - no error

I am not sure here myself using only %s instead of %s%0N does work for me but nanoseconds throws an error...

@baharestani

This comment has been minimized.

Copy link

@baharestani baharestani commented Apr 21, 2020

bad math expression: operator expected at 'N/1000000'

This happens on MAc because OSX doesn't support milliseconds. You should either remove %N and only get precision close to seconds, or install coreutils as explained here: https://apple.stackexchange.com/questions/135742/time-in-milliseconds-since-epoch-in-the-terminal

Alternatively you can just use 'time [your command here]` to see how long it takes

@timneutkens

This comment has been minimized.

Copy link

@timneutkens timneutkens commented May 3, 2020

Just to keep the "quick install" for Mac on this Gist.

Use Homebrew to install coreutils:

brew install coreutils

Then update date to gdate. The snippet becomes:

function preexec() {
  timer=$(($(gdate +%s%0N)/1000000))
}

function precmd() {
  if [ $timer ]; then
    now=$(($(gdate +%s%0N)/1000000))
    elapsed=$(($now-$timer))

    export RPROMPT="%F{cyan}${elapsed}ms %{$reset_color%}"
    unset timer
  fi
}
@sudocurse

This comment has been minimized.

Copy link

@sudocurse sudocurse commented Jun 10, 2020

An easier way to get around this on macOS is to use a prompt expansion format string. (i believe this requires clock_gettime and so it's only available on 10.12+). Additionally I can only seem to get millisecond granularity on mac (zsh should be able to go up to %9. decimal places; in fact zsh aliases %N to %9.):

function preexec() {
  timer=$(($(print -P %D{%s%6.})/1000))
}

function precmd() {
  if [ $timer ]; then
    now=$(($(print -P %D{%s%6.})/1000))
    elapsed=$(($now-$timer))

    export RPROMPT="%F{cyan}${elapsed}ms %{$reset_color%}"
    unset timer
  fi
}

It's pure zsh and this also saves you two forks every execution.

@jututt

This comment has been minimized.

Copy link

@jututt jututt commented Aug 20, 2020

Here's my take on the matter,
uses date +%s%3N to get milliseconds as base unit,
so no need to resort to division and consequent slimmer code:

function preexec() {
  timer=$(date +%s%3N)
}

function precmd() {
  if [ $timer ]; then
    now=$(date +%s%3N)
    elapsed=$(($now-$timer))

    export RPROMPT="%F{cyan}${elapsed}ms %{$reset_color%}"
    unset timer
  fi
}

--

Longer version for the timer to adapt to the command execution length (h/m/s/ms) and always have 3 digits accuracy.
Adapted from https://stackoverflow.com/questions/1862510/how-can-the-last-commands-wall-time-be-put-in-the-bash-prompt

function preexec() {
  timer=$(date +%s%3N)
}

function precmd() {
  if [ $timer ]; then
    local now=$(date +%s%3N)
    local d_ms=$(($now-$timer))
    local d_s=$((d_ms / 1000))
    local ms=$((d_ms % 1000))
    local s=$((d_s % 60))
    local m=$(((d_s / 60) % 60))
    local h=$((d_s / 3600))
    if ((h > 0)); then elapsed=${h}h${m}m
    elif ((m > 0)); then elapsed=${m}m${s}s
    elif ((s >= 10)); then elapsed=${s}.$((ms / 100))s
    elif ((s > 0)); then elapsed=${s}.$((ms / 10))s
    else elapsed=${ms}ms
    fi

    export RPROMPT="%F{cyan}${elapsed} %{$reset_color%}"
    unset timer
  fi
}
@rafbm

This comment has been minimized.

Copy link

@rafbm rafbm commented Dec 9, 2020

The following is perfect for me on macOS:

setopt prompt_subst

function preexec() {
  cmd_start=$(($(print -P %D{%s%6.}) / 1000))
}

function precmd() {
  if [ $cmd_start ]; then
    local now=$(($(print -P %D{%s%6.}) / 1000))
    local d_ms=$(($now - $cmd_start))
    local d_s=$((d_ms / 1000))
    local ms=$((d_ms % 1000))
    local s=$((d_s % 60))
    local m=$(((d_s / 60) % 60))
    local h=$((d_s / 3600))

    if   ((h > 0)); then cmd_time=${h}h${m}m
    elif ((m > 0)); then cmd_time=${m}m${s}s
    elif ((s > 9)); then cmd_time=${s}.$(printf %03d $ms | cut -c1-2)s # 12.34s
    elif ((s > 0)); then cmd_time=${s}.$(printf %03d $ms)s # 1.234s
    else cmd_time=${ms}ms
    fi

    unset cmd_start
  else
    # Clear previous result when hitting Return with no command to execute
    unset cmd_time
  fi
}

RPROMPT='%F{16}$(if [ $cmd_time ]; then echo "($cmd_time) "; fi)$(date "+%F %T %z")%F{none}'

The relevant variable is cmd_time. The full RPROMPT also contains the current date and time.

@ericbn

This comment has been minimized.

Copy link

@ericbn ericbn commented Mar 4, 2021

Here's a code that uses more features from Zsh, a refactoring of the code by @rafbm. No command substitutions are used.

zmodload zsh/datetime

prompt_preexec() {
  prompt_prexec_realtime=${EPOCHREALTIME}
}

prompt_precmd() {
  if (( prompt_prexec_realtime )); then
    local -rF elapsed_realtime=$(( EPOCHREALTIME - prompt_prexec_realtime ))
    local -rF s=$(( elapsed_realtime%60 ))
    local -ri elapsed_s=${elapsed_realtime}
    local -ri m=$(( (elapsed_s/60)%60 ))
    local -ri h=$(( elapsed_s/3600 ))
    if (( h > 0 )); then
      printf -v prompt_elapsed_time '%ih%im' ${h} ${m}
    elif (( m > 0 )); then
      printf -v prompt_elapsed_time '%im%is' ${m} ${s}
    elif (( s >= 10 )); then
      printf -v prompt_elapsed_time '%.2fs' ${s} # 12.34s
    elif (( s >= 1 )); then
      printf -v prompt_elapsed_time '%.3fs' ${s} # 1.234s
    else
      printf -v prompt_elapsed_time '%ims' $(( s*1000 ))
    fi
    unset prompt_prexec_realtime
  else
    # Clear previous result when hitting ENTER with no command to execute
    unset prompt_elapsed_time
  fi
}

setopt nopromptbang prompt{cr,percent,sp,subst}

autoload -Uz add-zsh-hook
add-zsh-hook preexec prompt_preexec
add-zsh-hook precmd prompt_precmd

RPS1='%F{cyan}${prompt_elapsed_time}%F{none}'
@mezza

This comment has been minimized.

Copy link

@mezza mezza commented Mar 23, 2021

How come you don't just use the timer plugin for oh-my-zshell? Just curious.

@digitaldonkey

This comment has been minimized.

Copy link

@digitaldonkey digitaldonkey commented Mar 24, 2021

<3 @mezza

@Joilence

This comment has been minimized.

Copy link

@Joilence Joilence commented Mar 24, 2021

Is it possible to add this execution time at the rear of the first line like the style of Spaceship Prompt?

@sudarshan85

This comment has been minimized.

Copy link

@sudarshan85 sudarshan85 commented May 4, 2021

Dumb question but whats the math for getting this in seconds? Change 1000000 to 1000?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment