Skip to content

Instantly share code, notes, and snippets.

@jpouellet jpouellet/zbell.sh
Last active Mar 10, 2019

Embed
What would you like to do?
Makes Zsh print a bell when long-running commands finish. I use this in combination with i3 and throw big compile jobs (or whatever it may be) into another workspace to get a nice visual notification (workspace indicator turns red) when it's done so I don't need to waste time regularly checking on it.
#!/usr/bin/env zsh
# This script prints a bell character when a command finishes
# if it has been running for longer than $zbell_duration seconds.
# If there are programs that you know run long that you don't
# want to bell after, then add them to $zbell_ignore.
#
# This script uses only zsh builtins so its fast, there's no needless
# forking, and its only dependency is zsh and its standard modules
#
# Written by Jean-Philippe Ouellet <jpo@vt.edu>
# Made available under the ISC license.
# only do this if we're in an interactive shell
[[ -o interactive ]] || return
# get $EPOCHSECONDS. builtins are faster than date(1)
zmodload zsh/datetime || return
# make sure we can register hooks
autoload -Uz add-zsh-hook || return
# initialize zbell_duration if not set
(( ${+zbell_duration} )) || zbell_duration=15
# initialize zbell_ignore if not set
(( ${+zbell_ignore} )) || zbell_ignore=($EDITOR $PAGER)
# initialize it because otherwise we compare a date and an empty string
# the first time we see the prompt. it's fine to have lastcmd empty on the
# initial run because it evaluates to an empty string, and splitting an
# empty string just results in an empty array.
zbell_timestamp=$EPOCHSECONDS
# right before we begin to execute something, store the time it started at
zbell_begin() {
zbell_timestamp=$EPOCHSECONDS
zbell_lastcmd=$1
}
# when it finishes, if it's been running longer than $zbell_duration,
# and we dont have an ignored command in the line, then print a bell.
zbell_end() {
ran_long=$(( $EPOCHSECONDS - $zbell_timestamp >= $zbell_duration ))
has_ignored_cmd=0
for cmd in ${(s:;:)zbell_lastcmd//|/;}; do
words=(${(z)cmd})
util=${words[1]}
if (( ${zbell_ignore[(i)$util]} <= ${#zbell_ignore} )); then
has_ignored_cmd=1
break
fi
done
if (( ! $has_ignored_cmd )) && (( ran_long )); then
print -n "\a"
fi
}
# register the functions as hooks
add-zsh-hook preexec zbell_begin
add-zsh-hook precmd zbell_end
@jpouellet

This comment has been minimized.

Copy link
Owner Author

jpouellet commented Apr 3, 2013

This has been incorporated into Joerg Jaspert's nice collection of ZSH config.

@teto

This comment has been minimized.

Copy link

teto commented Mar 24, 2018

Thanks for the gist. Would it be possible for the "zbell_ignore" check to expand aliases ? I have alias v=$EDITOR (for neovim) and even though $EDITOR is by default in zbell_ignore, it isn't matched when I run $ v todo.txt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.