# My .zshrc file
#
# Written by Matthew Blissett.
#
# Latest version available from https://matt.blissett.me.uk/linux/zsh/zshrc
#
# Some of this is my own creation, other functions are taken from various
# web sites or mailing lists, including:
# - http://leahneukirchen.org/dotfiles/.zshrc
#
# Last updated 2024-06-11
#
# Released into the public domain.
#
# Skip all this for non-interactive shells
[[ -z "$PS1" ]] && return
# Set prompt
# Exit status indicator in red (if not 0)
# Background job count in yellow (if not 0)
# Date in white, host in magenta, directory in default, prompt character
# Example:
# 1J 23:06:26 ig:~ >
PS1=$'%(?..%B%K{red}[%?]%K{def}%b )%(1j.%b%K{yel}%F{bla}%jJ%F{def}%K{def} .)%F{def}%B%*%b %F{mag}%m:%F{def}%~ %(!.#.>) %F{def}'
# <------- exit status -------><------- background job count ----------><--- time -->-< host ->-<- dir ->-<------> <----->
# (See 'EXPANSION OF PROMPT SEQUENCES' in zshmisc.)
# Completion system
autoload -Uz compinit
compinit
# Set less options
if [[ -x $(which less 2> /dev/null) ]]; then
export PAGER="less"
export LESS="--ignore-case --LONG-PROMPT --QUIET --chop-long-lines -Sm --RAW-CONTROL-CHARS --quit-if-one-screen --no-init"
export LESSHISTFILE='-'
if [[ -x $(which lesspipe 2> /dev/null) ]]; then
LESSOPEN="| lesspipe %s"
export LESSOPEN
fi
fi
# Set default editor
if [[ -x $(which emacs 2> /dev/null) ]]; then
export EDITOR="emacs"
export USE_EDITOR=$EDITOR
export VISUAL=$EDITOR
fi
# FAQ 3.10: Why does zsh not work in an Emacs shell mode any more?
# http://zsh.sourceforge.net/FAQ/zshfaq03.html#l26
[[ $EMACS = t ]] && unsetopt zle
# Zsh settings for history
HISTORY_IGNORE="(ls|[bf]g|exit|reset|clear|cd|cd ..|cd..)"
HISTSIZE=25000
HISTFILE=~/.zsh_history
SAVEHIST=100000
setopt INC_APPEND_HISTORY
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_REDUCE_BLANKS
setopt HIST_VERIFY
# nf [-NUM] [COMMENTARY...] -- never forget last N commands
nf() {
local n=-1
[[ "$1" = -<-> ]] && n=$1 && shift
fc -lnt ": %Y-%m-%d %H:%M ${*/\%/%%} ;" $n | tee -a .neverforget
}
# imgur - post image to imgur.com
# 20jul2014 +chris+ zsh function
# 01apr2016 +chris+ use api v3
imgur() {
curl -H "Authorization: Client-ID 3e7a4deb7ac67da" -F image=@$1 \
https://api.imgur.com/3/upload | sed 's/.*http/http/; s/".*/\n/; s,\\/,/,g'
}
# sprunge FILES... - paste to sprunge.us
# 20jul2014 +chris+ zsh function
sprunge() {
local f
if [ $# -lt 2 ]; then
cat "$@"
else
for f; do
echo "## $f"
cat "$f"
echo
done
fi | curl -sF 'sprunge=<-' http://sprunge.us | tr -d ' '
}
#http://chneukirchen.org/dotfiles/.zshrc
# Say how long a command took, if it took more than 30 seconds
export REPORTTIME=120
# Update the prompt (i.e. the clock) before executing a command
TRAPDEBUG() {
case "$WIDGET" in
zle-line-finish)
zle reset-prompt
;;
*)
;;
esac
}
# Colour output on Mac OS
export CLICOLOR=1
# Fix stupid SystemD non-standard colours
export SYSTEMD_COLORS=16
# Zsh spelling correction options
#setopt CORRECT
#setopt DVORAK
# Prompts for confirmation after 'rm *' etc
# Helps avoid mistakes like 'rm * o' when 'rm *.o' was intended
setopt RM_STAR_WAIT
# Background processes aren't killed on exit of shell
setopt AUTO_CONTINUE
# Don’t write over existing files with >, use >! instead
setopt NOCLOBBER
# Don’t nice background processes
setopt NO_BG_NICE
# Watch other user login/out
watch=notme
export LOGCHECK=60
# Stop at / when deleting
export WORDCHARS='*?_-.[]~=&;!#$%^(){}<>'
# Enable color support of ls
if [[ "$TERM" != "dumb" ]]; then
if [[ -x `which dircolors 2> /dev/null` ]]; then
eval `dircolors -b`
if ls --color &> /dev/null; then
alias 'ls=ls --color=auto'
fi
fi
fi
# Why is the date American even when the locale is en_GB? Choose ISO form anyway.
export TIME_STYLE="long-iso"
# Commas in ls, du, df output
export BLOCK_SIZE="'1"
# No quoting spaces in newer coreutils
export QUOTING_STYLE=literal
# Short command aliases
alias 'l=ls'
alias 'la=ls -A'
alias 'll=ls -l'
alias 'llh=ls -l --si'
alias 'lq=ls -Q'
alias 'lr=ls -R'
alias 'lrs=ls -lrS'
alias 'lrt=ls -lrt'
alias 'lrta=ls -lrtA'
alias 'lrth=ls -lrth --si'
alias 'lrtha=ls -lrthA --si'
alias 'j=jobs -l'
alias 'kw=kwrite'
alias 'tf=tail -F'
alias 'grep=grep --colour --devices=skip --exclude-dir=.git'
alias 'vnice=nice -n 20 ionice -c 3'
alias 'get_iplayer=get_iplayer --nopurge'
alias 'get-iplayer=get-iplayer --nopurge'
alias "tree=tree -I 'CVS|.git|*~'"
alias 'lo=z loffice'
alias 'idea=z intellij-idea-community'
alias 'synchist=fc -RI'
# Useful KDE integration
alias 'k=kate -u' # -u is reuse existing session if possible
alias 'white=konsoleprofile colors=SolarizedLight'
alias 'black=konsoleprofile colors=Linux'
# These are useful with the Dvorak keyboard layout
alias 'h=ls'
alias 'ha=la'
alias 'hh=ll'
alias 'hhh=llh'
alias 'hq=lq'
alias 'hr=lr'
alias 'hrt=lrt'
alias 'hrs=lrs'
# Play safe!
alias 'rm=rm -i'
alias 'mv=mv -i'
alias 'cp=cp -i'
# For Git
alias 'gk=z gitk --all'
alias 'gs=git status' # (NB overriding GhostScript)
alias 'gd=git diff'
alias 'gg=z git gui'
alias 'git-stashpullpop=git stash && git pull --rebase && git stash pop'
alias 'gl=git log --graph --abbrev-commit --pretty=oneline --decorate'
# For convenience
alias 'aspell=aspell --dont-backup'
alias 'mkdir=mkdir -p'
alias 'cal=ncal -b' # Weeks start on Monday
alias 'dmesg=dmesg --ctime'
alias 'df=df --exclude-type=tmpfs --exclude-type=devtmpfs --exclude-type=squashfs'
alias 'd.=df -h . |sed 1d'
alias 'dus=du -msc * .*(N) | sort -n'
alias 'dus.=du -msc .* | sort -n'
alias 'fcs=(for i in * .*(N); do echo $(find $i -type f | wc -l) "\t$i"; done) | sort -n'
alias 'fcs.=(for i in .*; do echo $(find $i -type f | wc -l) "\t$i"; done) | sort -n'
alias 'last=last -a'
alias 'zap=clear; echo -en "\e[3J"'
alias 'rmedir=rmdir -v **/*(/^F)'
alias 'xmlindent=xmlindent -t -f -nbe'
alias 'psqltsv=psql -X -t -A -F" "'
alias 'qr=qrencode -t UTF8'
# Typing errors...
alias 'cd..= cd ..'
# Global aliases (expand whatever their position)
# e.g. find . E L
alias -g :E='2> /dev/null'
alias -g :C='| wc -l'
alias -g :H='| head'
alias -g :L='|& less'
alias -g :S='| sort'
alias -g :SN='| sort -n'
alias -g :T='| tail'
unglobalalias() {
for i in ':E' ':C' ':H' ':L' ':S' ':SN' ':T'; unalias $i
}
# Log file viewing
lastlogdir=logs
alias taillast='tail -f $lastlogdir/*(om[1])'
alias catlast='< $lastlogdir/*(om[1])'
alias lesslast='less $lastlogdir/*(om[1])'
# Build commands, etc
alias gdoc='docker run --rm -it --user $(id -u):$(id -g) -v $PWD:/documents/ docker.gbif.org/asciidoctor-toolkit build'
alias gdocfast='docker run --rm -it --user $(id -u):$(id -g) -v $PWD:/documents/ docker.gbif.org/asciidoctor-toolkit build --no-pdf --no-compress --no-git --no-spellcheck --no-translation'
alias 'gdoc-continuous'='docker run --rm -it --user $(id -u):$(id -g) -v $PWD:/documents/ docker.gbif.org/asciidoctor-toolkit continuous'
alias hp='docker run -e JEKYLL_ENV=development -e JEKYLL_UID=$(id -u) -e JEKYLL_GID=$(id -g) --rm --volume="$PWD:/srv/jekyll" jekyll/jekyll:4.1.0 bash -c "gem sources --add https://repository.gbif.org/repository/rubygems.org/ && gem sources --remove https://rubygems.org/ && bundle config mirror.https://rubygems.org https://repository.gbif.org/repository/rubygems.org && echo && echo && echo && echo BUILD STARTING: && jekyll build --unpublished --strict-front-matter"'
# Quick TSV/CSV file formatting
tsv() {
if [[ -t 1 ]]; then
column --separator $'\t' --table $* | less
else
column --separator $'\t' --table $*
fi
}
tsvn() {
dir=$(mktemp -d)
mkfifo $dir/firstline
cat $* | tee $dir/firstline | \
(seq -f '%g ' $(head -n 1 $dir/firstline | tr -dc '\t' | awk '{print length+1}') | tr -d '\n' | sed 's/ $/\n/'; \
cat) \
| tsv
rm -Rf $dir
}
csv() {
# csvtool
if [[ -t 1 ]]; then
csvtool readable $* | less
else
csvtool readable
fi
}
# chop - limit lines to terminal size
# 14jun2016 +chris+
chop() { cut -c-${COLUMNS:-80} }
# SSH aliases
alias 'sshb=ssh matt@uc.blissett.me.uk'
alias 'sshstop=ssh -O stop'
alias 'c3g=ssh mblissett@c3gateway-vh.gbif.org'
alias 'c4g=ssh mblissett@c4gateway-vh.gbif.org'
alias 'c5g=ssh mblissett@c5gateway-vh.gbif.org'
# SSH to shell[1234].doc.ic.ac.uk at random
sshdoc() {
ssh mrb04@shell$(($RANDOM % 4 + 1)).doc.ic.ac.uk $*
}
# Remove a known host and its IP from the cache
ssh-knownhosts-remove() {
ssh-keygen -R $1
ssh-keygen -R $(dig $1 +short)
ssh -O stop $1 &> /dev/null
}
ssh-knownhosts-remove-by-line() {
echo "Not implemented"
# sed -e '800d' | sponge...?
}
# Automatically background processes (no output to terminal etc)
z () {
grey='\e[1;30m'
norm='\e[m'
outfile=$(mktemp --tmpdir ${1//\//}.XXX)
echo "$grey$* &> $outfile$norm"
$* &>! $outfile &!
}
compdef z=sudo
# Aliases to use this
# Use e.g. 'command gv' to avoid
for i in acroread akregator amarok ario audacity chromium darktable dolphin easytag eclipse \
firefox gimp gpdf gpsprune gv gwenview hugin inkscape kate kmag konqueror ktorrent kwrite \
libreoffice lobase localc lodraw loffice lomath loffice lowriter \
okular openscad prusa-gcodeviewer prusa-gcodeviewer-alpha prusa-gcodeviewer-xz prusa-slicer prusa-slicer-alpha \
qgis see skype skypeforlinux super-slicer thunderbird vlc; do
alias "$i=z $i"
done
# Quick find
f() {
echo "find . -iname \"*$1*\""
find . -iname "*$1*"
}
# Quick regex history search
zh() {
pattern=^$(echo '(?=.*'${^@}')' | tr -d ' ')
grep --text ~/.zsh_history --perl-regexp --regexp $pattern
}
# Remap Dvorak-Qwerty quickly
alias 'aoeu=setxkbmap us -option' # (American keyboard layout, no special options)
alias 'asdf=setxkbmap us dvorak -option compose:menu,terminate:ctrl_alt_bksp'
# Change between English and Danish
english() {
export LANG=en_GB.UTF-8
export LANGUAGE=en_GB:en
}
danish() {
export LANG=da_DK.UTF-8
export LANGUAGE=da_DK:da
}
# Bring emacs to foreground, or else edit last-modified file
e() {
if [[ -n "$1" ]]; then
files=( **/*$1*(.) )
if [[ $#files = 1 ]]; then
echo Selected $files[1]
emacs $files[1]
else
echo Matched multiple files:
echo $files
fi
else
jobs emacs && fg emacs || emacs *(.om[1]^D)
fi
}
# Change terminal title
title() {
echo -ne "\033]30;$*\007"
}
# Update config files (master copies stored on server)
alias pull-config='(cd ~/.matt-config; git --git-dir=$HOME/.matt-config/.git pull --rebase && . ~/.zshrc)'
pull-config-regularly() {
if [[ -z "$SSH_CONNECTION" ]]; then
if after 1 day $HOME/.pull-config-regularly; then
echo "Pulling config"
pushd ~/.matt-config
git pull --rebase
popd
touch $HOME/.pull-config-regularly
fi
fi
}
pull-config-regularly
# Check dot-files are up-to-date
~/.matt-config/make-links
# Named directories
hash -d config=$HOME/.matt-config
hash -d bin=$HOME/.matt-config/bin
hash -d log=/var/log
# mkcd -- mkdir and cd at once
mkcd() { mkdir -p -- "$1" && cd -- "$1" }
compdef mkcd=mkdir
# When directory is changed set xterm title to host:dir
chpwd() {
[[ -t 1 ]] || return
case $TERM in
sun-cmd) print -Pn "\e]l%~\e\\";;
*xterm*|rxvt|(dt|k|E)term) print -Pn "\e]2;%m:%~\a";;
esac
}
# For changing the umask automatically
chpwd () {
case $PWD in
$HOME/[Dd]ocuments*)
if [[ $(umask) -ne 077 ]]; then
umask 0077
echo >&2 -e "\033[01;32mumask: private \033[m"
fi;;
/nothing)
if [[ $(umask) -ne 072 ]]; then
umask 0072
echo >&2 -e "\033[01;33mumask: other readable \033[m"
fi;;
/nothing)
if [[ $(umask) -ne 002 ]]; then
umask 0002
echo >&2 -e "\033[01;35mumask: group writable \033[m"
fi;;
*)
if [[ $(umask) -ne 022 ]]; then
umask 0022
echo >&2 -e "\033[01;31mumask: world readable \033[m"
fi;;
esac
}
cd . &> /dev/null
for d in ~/GBIF/Workspace ~/Workspace; do
if [[ -d $d ]]; then
cdpath=( $d $d/gbif-documents )
export cdpath
hash -d Workspace=$d
# Used below for hostname completion
workspace=$d
fi
done
# For quickly plotting data with gnuplot. Arguments are files for 'plot "