A few years ago I was using ZSH with Sindre Sorhus’s Pure prompt and
generally enjoying the experience. The big, dumb, obvious caveat of
using ZSH is that it’s not Bash. As a result, when you SSH into
production machines that only have bash installed, you feel a little
off-balance. Off-balance is not a feeling you want during an
emergency on a live application server. As a result, I switched back to
using bash everywhere. I never really missed fancy syntax highlighting, or the nice globbing
features, or most of ZSH really. The one thing I missed immensely was
not so much a feature of ZSH as it was a feature of the Pure prompt I
had been using: execution time for long-running commands shown in the
prompt. The Command execution time in Pure uses the Using these two tools in conjunction, you can approximate
So now debug is executed before each “simple command” and
Timing each command is then pretty trivial: Integrating that into the prompt and making it look pretty is just a
little-bit of code away: Motherfucking magic.How I learned to stop worrying and love bash.
time(1)
command is a command that I never think to
run until it’s too late. The
Pure prompt is fancy. By default, if a particular command takes
longer than 5 seconds to run, Pure calculates the running time of that
command, and renders a human-readable version just above your current
prompt.~
❯ sleep 5
~ 5s
❯ sleep 10
~ 10s
❯
Bash it until it works
preexec
and
precmd
feature of ZSH, which doesn’t exist in Bash. Instead
Bash has the less intuitive trap
command and the
PROMPT_COMMAND
shell variable.PROMPT_COMMAND
is a variable that can be set in your
bash config file that, “is executed as a command prior to issuing each
primary prompt.” [bash(1)]trap [-lp] [arg] [sigspec]
can be used to listen for the
DEBUG
signal. “If a sigspec is DEBUG, the command arg is
executed before every simple command”preexec
and precmd
:debug() {
# do nothing if completing
[ -n "$COMP_LINE" ] && return
# don't cause a preexec for $PROMPT_COMMAND
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
echo 'debug'
}
prompt() {
echo 'prompt'
}
trap 'debug' DEBUG
PROMPT_COMMAND=prompt
prompt
is executed before each issuing the primary
prompt.$ echo 'hi'
debug
hi
prompt
debug() {
# do nothing if completing
[ -n "$COMP_LINE" ] && return
# don't cause a preexec for $PROMPT_COMMAND
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
start_time=$(date +'%s')
}
prompt() {
end_time=$(date +'%s')
echo "$(( end_time - start_time )) seconds"
}
trap 'debug' DEBUG
PROMPT_COMMAND=prompt
$ sleep 2
2 seconds
$
# Human readable time output
# e.g., 5d 6h 3m 2s
format_time() {
local _time=$1
# Don't show anything if time is less than 5 seconds
(( $_time < 5 )) && return
local _out
local days=$(( $_time / 60 / 60 / 24 ))
local hours=$(( $_time / 60 / 60 % 24 ))
local minutes=$(( $_time / 60 % 60 ))
local seconds=$(( $_time % 60 ))
(( $days > 0 )) && _out="${days}d"
(( $hours > 0 )) && _out="$_out ${hours}h"
(( $minutes > 0 )) && _out="$_out ${minutes}m"
_out="$_out ${seconds}s"
printf "$_out"
}
debug() {
# do nothing if completing
[ -n "$COMP_LINE" ] && return
# don't cause a preexec for $PROMPT_COMMAND
[ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return
start_time=$(date +'%s')
}
prompt() {
end_time=$(date +'%s')
time_f=$(format_time $(( end_time - start_time )))
PS1="${time_f} (•◡•)❥"
}
trap 'debug' DEBUG
PROMPT_COMMAND=prompt
(•◡•)❥ sleep 5
5s (•◡•)❥
Posted