Shell tricks: shorthands
Even with tab completion, typing long commands is tedious. But, there’s something even worst: typing the same long commands again, and again, and again… So how do you solve that? It’s simple: you shorten them. Surprising, uh? Okay enough theory, let me show you some examples.
Here’s a tedious command of Type-A:
% sudo aptitude install zsh
Look at it carefully since you will need to hunt these long commands
down until none remains. Now, let me explain how you execute a such
command. Open up your personal shell initialization file
(e.g. ~/.bashrc
for Bash, ~/.zshrc
for Zsh, etc). Then, add the
following:
alias spkgi="sudo aptitude install"
Reload your shell and finally, enjoy:
% spkgi zsh
Now I can introduce, as you can deduce, other shorten commands that you can produce and reproduce:
# Package Management
alias pkg="aptitude"
alias spkg="sudo aptitude"
alias spkgi="sudo aptitude install"
alias spkgu="sudo aptitude safe-upgrade"
alias spkgr="sudo aptitude remove"
alias spkgd="sudo apt-get build-dep"
# Miscellaneous Helpers
alias nc="rlwrap nc"
alias e=$EDITOR
alias se=sudoedit
alias reload="source ~/.zshrc"
alias g=egrep
Next after Type-A tedious commands, we have the Type-S ones. To execute these, you will you need some sort of special shell support. So, here’s some examples of the Type-S monstrosity:
% find Lib/ -name '*.c' -print0 | xargs -0 grep ^PyErr
% find -name '*.html' -print0 | xargs -0 rename 's/\.html$/.var/'
% find -name '*.patch' -print0 | xargs -0 -I {} cp {} patches/
I hope you start to see some patterns (if you don’t, then try harder). The first one could (and should) be rewritten as:
% rgrep --include='*.c' ^PyErr Lib/
But that isn’t short enough for me, so I have a short helper:
rg()
{
filepat="$1"
pat="$2"
shift 2
grep -Er --include=$filepat $pat ${@:-.}
}
# In Zsh, 'noglob' turns off globing.
# (e.g, "noglob echo *" outputs "*")
alias rg='noglob rg'
It is lovely to use:
% rg *.c ^PyErr Lib/
% rg *.c PyErr_Restore . -C 10 | less
% rg *.[ch] stringlib
% rg *.c ^[a-zA-Z]*_dealloc Modules/ Objects/
The second example is quite similar to the previous one. However, the
find/rename combination is much less common (at least for me) than the
find/grep one. This one needs to be broken in pieces. One obvious thing
to factor out is the find -name
with an alias:
alias fname="noglob find -name"
Using this alias, you can rewrite the second example as:
% fname *.html -print0 | xargs -0 rename 's/\.html$/.var/'
It’s better, but it’s not short enough yet. The ugly part of this
command is the -print0 | xargs -0
. I hate to type that. Wouldn’t
it be nice if we could define an alias for it? How about:
alias each="-print0 | xargs -0"
Unfortunately, that doesn’t work since aliases are only expanded if they are in the command position. Luckly, Zsh has that neat feature called global aliases, which does exactly what we want.
alias -g each="-print0 | xargs -0"
With this feature of Zsh, the second example become:
% fname *.html each rename 's/\.html$/.var/'
Now, we can also attack the third one:
% fname *.patch each -I {} cp {} patches/
It is possible to shorten a bit by defining another alias combining
each
and -I {}
, but that won’t make a big difference.
Finally, there are the Type-R tedious commands. These are hard to avoid, unless you’re careful. Here’s again some ridiculous examples to help you recognize these redundant commands:
% gcc -o stackgrow stackgrow.c
% pkg show emacs-snapshot-bin-common emacs-snapshot-common emacs-snapshot-gtk emacs-snapshot
% cat ../lispref.patch ../lwlib.patch ../etc.patch | patch -p1
To reduce these, you don’t need change your shell configuration; you change your habits instead. Using alternations (which are non-standard, but supported by most shells), you can rewrite the two first example as:
% gcc -o stackgrow{,.c}
% pkg show emacs-snapshot{{-bin,}-common,-gtk,}
Now, you are surely asking yourself: “what is different about the third one?” Well, think about it. Got it? No? Ah, come on, it is easy. Here’s a hint:
% echo 'cat ../{lispref,lwlib,etc}.patch | patch -p1' | wc -c
45
% echo 'cat ../lispref.patch ../lwlib.patch ../etc.patch | patch -p1' | wc -c
61
You like my hint, don’t you? Here’s the answer:
% echo 'cat ../li\t ../lw\t ../et\t | patch -p1' | wc -c
37
Tab completion doesn’t work well with prefix alternations. Even if the command using alternation is shorter, it still doesn’t beat good old tab completion.
And that’s all folks. I surely have plenty of other tricks to show, but that will be for the other posts of this short series.