A few years ago, I tried using The Friendly Interactive SHell as my default shell.
It has a lot of nice things, like syntax highlighting on the command line, reverse-history-search by pressing the up/down arrows, and a few more things, but it kept rubbing me the wrong way that it is not POSIX compatible (like bash or zsh), so I ended up going back to zsh.
I did, however, bring a few features with me because they are awesome, and abbreviations is one of those features. They are sort of like aliases but better, at least in my opinion.
Abbreviations vs aliases
In all shells there is something called aliases.
Aliases work like this, you define some alias, like alias gst="git status"
. After this, if I type gst
into my terminal and hit enter, the terminal will run the git status
command and display the output as expected.
In fish, I can also define an abbreviation like this:
abbr -a -g gst git status
It works mostly the same way as an alias but with one very important exception: if I type gst
in my terminal and hit space, the terminal will expand the abbreviation. If I hit enter instead of space, it will expand the abbreviation and run the command.
This means that it's easier to see what commands have been run by looking at the history and seeing a bunch of lines with git status
instead of just gst
, and it also means that I can copy commands that I ran directly from my terminal, send them to a collegue, and they can just paste and run them directly if they want to. They don't need to know anything about what aliases I happen to have in my shell config.
How to get this in Zsh?
I simply add these functions in my ~/.zshrc
and then I can start listing abbreviations:
# declare a list of expandable aliases to fill up later
typeset -a ealiases
ealiases=()
# write a function for adding an alias to the list mentioned above
function abbrev-alias() {
alias $1
ealiases+=(${1%%\=*})
}
# expand any aliases in the current line buffer
function expand-ealias() {
if [[ $LBUFFER =~ "\<(${(j:|:)ealiases})\$" ]]; then
zle _expand_alias
zle expand-word
fi
zle magic-space
}
zle -N expand-ealias
# Bind the space key to the expand-alias function above, so that space will expand any expandable aliases
bindkey ' ' expand-ealias
bindkey '^ ' magic-space # control-space to bypass completion
bindkey -M isearch " " magic-space # normal space during searches
# A function for expanding any aliases before accepting the line as is and executing the entered command
expand-alias-and-accept-line() {
expand-ealias
zle .backward-delete-char
zle .accept-line
}
zle -N accept-line expand-alias-and-accept-line
Then I can start adding abbreviations like so:
abbrev-alias g="git"
abbrev-alias gst="git status"
abbrev-alias gcb="git checkout --branch"
abbrev-alias ll="ls -l"
Top comments (6)
Created an account just to reply to this. Incredible! Tried out fish just now and had a horrendous experience trying to set things up. The only positive of fish for me were the abbreviations. I always write out stuff like
dotnet new sln -n
and so on, because relying on aliases too much might make me forget the actual command lol. Now with this code, I can continue using zsh while having the amazing feature of abbreviations, thanks to you and to noahdotpy for sharing!Amazing! I'm going full ZSH babyyy.
Thank you for this amazing feature, a good friend recommended this, now I don't use any alias only abbrev-alias 😊
I will just be changing this a bit so that I can do <command> to cancel expand just like I can when I do aliases. Here is my changed code that I did to achieve this. I also added so that it makes a variable so that if you want, you can do something like (with
abbrev-alias $nvimconf="$HOME/.config/nvim/"
)cd $nvimconf
New code:
Yo, thank you!!!!!!!! This works perfectly! Abbreviations were the last things keeping me on fish, now I'm permanently going to zsh.
Nice to hear. I’m happy that it helped someone else :)