Skip to content

Display of the execution time for commands in the right prompt of the Z shell

License

Notifications You must be signed in to change notification settings

TomfromBerlin/zsh-cmd-time

Repository files navigation

zsh-cmd-time

Views Unique Viewers https://img.shields.io/github/license/TomfromBerlin/zsh-cmd-time?style=plastic https://img.shields.io/github/v/release/TomfromBerlin/zsh-cmd-time?include_prereleases&style=plastic

zsh-cmd-time_test

Introduction

Actually, this is a fork of zsh-command-time plugin made by Stanislav Popov, who honestly seems to be a much more talented developer than me.

I created this fork because I wanted to have a display of the command execution time in my RPS1, but I do not want to use frameworks like antigen or powerlevel9k/p10k, although they are good and sophisticated frameworks for the Z shell.

After playing around with the code the two branches started to differ greatly and it would break zsh-command-time plugin if it were merged with my code. In addition, his repository no longer seems to be maintained, so I decided to detach this repository from its origin and create a standalone repository. Nevertheless, popstas deserves most of the credits, because without his code, zsh-cmd-time would not exist at all.

What distinguish this fork from the original?

... One is that the original is probably bash-compatible. Unfortunately, this is not possible with this repository without bloating the code. That is, this plugin runs only with the Z Shell without any problems.

The orignal plugin, on the other hand, only displays whole seconds. This fork can display fractions of seconds. It's up to you whether you want that or not.

In addition, the original is somewhat easier to configure with regard to the color scheme and the output whether very short program execution times should be displayed.

Description of zsh-cmd-time

zsh-cmd-time is a plugin that outputs the execution time of commands and exports the result to a variable that can be used elsewhere. It is similar to the built-in REPORTTIME function, but it is also slightly different.

REPORTTIME is a nifty feature of zsh. If you set it to a non-negative value, then every time, any command you run that takes longer than the value you set it to in seconds, zsh will print usage statistics afterwards as if you had run the command prefixed with time. Well, almost every time.

The following screenshot shows two measurements, both with REPORTTIME=3, but REPORTTIME itself remains silent. The output would look like that of the second measurement but that comes from time command. (Just ignore the right prompt at this point.)

reporttime

As mentioned before REPORTTIME has been set to REPORTTIME=3 (seconds) and one would expect a response by REPORTTIME. However, sleep does not consume any CPU time and REPORTTIME does not recognize such idle commands. Here zsh-cmd-time comes into play. As you can see, the right prompt shows the execution time regardless of whether CPU time was consumed or not and this is the plugin at work.

So if you want to monitor CPU-consuming commands only, you should use REPORTTIME instead of this plugin.

At this point, it is probably worth mentioning that the measured times do vary, especially with many decimal places, even if the same program is executed twice directly in succession. Well, the measurement is not done with a high precision clock, but with a computer with many different components of hardware and software, which can influence the results differently at different times. In addition, there are rounding errors, which are unavoidable due to the way floating point numbers are handled in a digital environment. For this reason, the measured times are to be understood rather as approximate values. If desired, it is recommended to perform a series of measurements from which an average value can then be calculated.

Installation

My first recommendation is: don't use a plugin manager or framework at all if you only want to use a few plugins to improve your daily zsh experience. Instead you can use zsh_unplugged. zsh_unplugged just contains around 20 lines of code. All you need is adding a few lines to your .zshrc-file:

Code
```
# ZSH UNPLUGGED start (first part)
# where do you want to store your plugins?
ZPLUGINDIR=${ZDOTDIR:-~/.config/zsh}/plugins
# get zsh_unplugged and store it with your other plugins
if [[ ! -d $ZPLUGINDIR/zsh_unplugged ]]; then
  git clone --quiet https://github.com/mattmc3/zsh_unplugged $ZPLUGINDIR/zsh_unplugged
fi
source $ZPLUGINDIR/zsh_unplugged/zsh_unplugged.zsh

# make list of the Zsh plugins you use (the order of the list can be important, it depends on the plugins used)
repos=(
    $ZPLUGINDIR/zsh-enhanced-completion # this is a local plugin
    TomfromBerlin/zsh-cmd-time # this plugun will be cloned from Github
    )
# ZSH UNPLUGGED end (first part)
```

and before `autoload -Uz promptinit && promptinit`, add

```
# ZSH UNPLUGGED start (second part)
plugin-load $repos
# ZSH UNPLUGGED end (second part)
```

❗ This plugin will replace your RPS1 definition. To avoid this, remove the strings below and add ${elapsed} or ${timer_show} to your RPS1. ❗

RPS1='%(?.%F{green}√.%K{red}%F{black} Nope!)%f%k'
RPS1='${elapsed} %(?.%F{green}√.%K{red}%F{black} Nope!)%f%k'

Of course, you can use it in your PS1. At this point, it is quite helpful to have a little knowledge about how to customize the prompt. If you only want to see the execution time without prefixed text you can use the timer_show variable.

You may want to consider to run the script zrecompile to compile all the zsh-dot-files to give your shell another performance boost. A descrption of what the script does can be found within the source file. The line autoload -Uz [$HOME]/path/to/script/zrecompile, placed somewhere in your .zshcr, may be helpful when (re)running the script, e.g. after changing dot-files.

Install with other plugin managers/frameworks

show instructions

zplugin

Zplugin offers a few convenient functions around plugin management.

At first you need to install zplugin. To do this perform the following steps:

mkdir ~/.zplugin
git clone https://github.com/TomfromBerlin/zplugin.git ~/.zplugin/bin # The original "zplugin" plugin manager repository no longer exists. Be aware that there is no support for "zplugin".

and add

source ~/.zplugin/bin/zplugin.zsh # should be called before compinit
zmodload zsh/complist # should be called before compinit, the directory `zsh` should be in your $FPATH

before loading completion settings as well as

autoload -Uz compinit && compinit -C -d ${zdumpfile}
zplugin cdreplay -q # -q is for quiet

after loading completion settings.

Then add zplugin load TomfromBerlin/zsh-cmd-time to your .zshrc to install the cmd-time plugin. Best practice: place it before your prompt definitions. Next time you start a terminal zplugin downloads the plugin and compiles it with zcompile, giving your shell a noticeable performance boost.

Install with antigen

antigen bundle TomfromBerlin/zsh-cmd-time

Install for oh my zsh

Download:

git clone https://github.com/TomfromBerlin/zsh-cmd-time.git ~/.oh-my-zsh/custom/plugins/cmd-time

And add cmd-time to plugins in .zshrc.

Usage with powerlevel9k theme

To make it short: Do not use this plugin with powerlevel9k/p10k

powerlevel9k as of v0.6.0 has a native segment of command_execution_time, so you can easily add it to your prompt:

POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status background_jobs vcs command_execution_time time)

Configuration

You do not need to configure the plugin. It should run out of the box. You can override some defaults in .zshrc:

# Pefixed message to display (set to "" for disable).
ZSH_CMD_TIME_MSG="took %s"

# Exclude some commands
ZSH_CMD_TIME_EXCLUDE=(clear cls man mc mcedit nano ranger vim)

If you want to have an hourglass ⌛ instead of text in front of the execution time you can use the following syntax:

ZSH_CMD_TIME_MSG="\uF253$s %s"

However, you need to install Symbols Nerd Font Mono or DroidSansMono Nerd Font for this. The Nerdfonts Cheat Sheet can be used to find your desired icon (e.g., search for "hour"). The site then also provides the corresponding UTF-16 code.

Customization

You can also customize the output of the plugin by redefining the zsh_command_time function. Here are two examples of custom definitions.

The configuration below can handle floating point numbers and will display decimal places for short commands:

  • 6 decimal places when exec time is < 1 second
  • 3 decimal places when exec time is between 1 second and 60 seconds

'%.nf' defines the number of decimal places, where n is an integer. However, the computer's internal representation of floating point numbers has a limited number of bits and as a consequence a limited accuracy. So numbers with floating point cannot be stored as e.g. 3.0000000000 in memory, but as 3.0000000002 or 2.9999999998. Rounding errors are therefore unavoidable.

Longer execution times will be displayed as "mm:ss", or "hh:mm:ss" respectively. When execution time is above 60 seconds, neither milliseconds nor nanoseconds are displayed.

Output with colors (default configuration)

zsh_command_time() {
  if [ -n "$ZSH_COMMAND_TIME" ]; then
  h=$(bc -l <<< $ZSH_COMMAND_TIME/3600)
  m=$(bc -l <<< $ZSH_COMMAND_TIME%3600/60)
  s=$(bc -l <<< $ZSH_COMMAND_TIME%60)
  if [[ "$timer_show" -le "1" ]]; then ZSH_CMD_TIME_COLOR="magenta" && timer_show=$(printf '%.6f'" sec" "$timer_show")
        elif [[ "$timer_show" -le "60" ]]; then ZSH_CMD_TIME_COLOR="green" && timer_show=$(printf '%.3f'" sec" "$timer_show")
        elif [[ "$timer_show" -gt "60" ]] && [[ "$timer_show" -le "180" ]]; then ZSH_CMD_TIME_COLOR="cyan" && timer_show=$(printf '%02dm:%02ds' $((m)) $((s)))
        elif [[ "$h" -gt 0 ]]; then m=$((m%60)) && ZSH_CMD_TIME_COLOR="red" && timer_show=$(printf '%02dh:%02dm:%02ds' $((h)) $((m)) $((s))); else ZSH_CMD_TIME_COLOR="yellow" && timer_show=$(printf '%02dm:%02ds' $((m)) $((s)))
        fi
    elapsed=$(echo -e "%F{$ZSH_CMD_TIME_COLOR}$(printf '%s' "${ZSH_CMD_TIME_MSG}"" $timer_show")%f")
    export elapsed
    RPS1='${elapsed} ${vcs_info_msg_0_} %(?.%F{green}√.%K{red}%F{black} Nope!)%f%k'
  fi
}

The output on the right prompt in the Z shell looks like this:

zsh-cmd-time

You can change the colors, too. Just look for "cyan", "green", "magenta", "red", and "yellow" and replace it with your desired colors. Double quotes are required.

Annotation
When using print -P in the right prompt of the Z shell with the above configuration, it happened that the output was severely out of place. Unfortunately, print -P moves the right prompt towards the center of the window and I have not found a solution for this yet, except to replace print -P with echo -e. Maybe this is the only solution, who knows. I tried to fix it with %{$elapsed%}, but that moves RPS1 too much to the right and then causes an unwanted line break. So there is a mix of echo -e and printf on one line, which looks pretty stupid - but works and even shellcheck do not complain.

Output without colors

zsh_cmd_time() {
    if [[ -n "$ZSH_CMD_TIME" ]]; then
# we leave the handling of floating point numbers to bc --> https://www.gnu.org/software/bc/manual/html_mono/bc.html
        h=$(bc <<< "${timer_show}/3600") && m=$(bc <<< "(${timer_show}%3600)/60") && s=$(bc <<< "${timer_show}%60")
        if [[ "$ZSH_CMD_TIME" -le 1 ]]; then timer_show=$(printf %.6f" sec" "$ZSH_CMD_TIME")
        elif [[ "$ZSH_CMD_TIME" -le 60 ]]; then timer_show=$(printf %.3f" sec" "$ZSH_CMD_TIME")  # for explanation of "%.nf" see configuration example above
        elif [[ "$h" -gt 0 ]]; then m=$((m%60)) && timer_show=$(printf '%dh:%02dm:%02ds' $((h)) $((m)) $((s)))
        else timer_show=$(printf '%02dm:%02ds' $((m)) $((s)))
        fi
        elapsed=$(printf '%s' "${ZSH_CMD_TIME_MSG}"" $timer_show")
        export elapsed
        RPS1='${elapsed} ${vcs_info_msg_0_} %(?.%F{green}√.%K{red}%F{black} Nope!)%f%k'
    fi
}

And now have fun and be nice to each other.