The Zsh Pony ============
The Zsh defaults to a minimalistic configuration which doesn’t show the potential behind this powerful and flexible shell. The Zsh pony project provides a list of really hot stuff of what’s possible with Zsh.
Grab a fully featured Zsh configuration:
% wget -O .zshrc http://git.grml.org/f/grml-etc-core/etc/zsh/zshrc
% setopt autocd && /tmp
% setopt append_history share_history histignorealldups
% setopt $OPTION % man zshoptions
% autoload -U zmv % touch 1\ 2 3\ 4\ 5 % zmv '* *' '$f:gs/ /_'
% autoload run-help % echo foo | xargs <esc-h> and then: % git commit<esc-h> or even ('g' being an alias for git and 'co' and git alias for commit): % g co<esc-h>
% autoload compinit && compinit % kill c<tab> % man z<tab> % dpkg -L <tab>
% zstyle ':completion:*' menu select
Layout is :completion:FUNCTION:COMPLETER:COMMAND-OR-MAGIC-CONTEXT:ARGUMENT:TAG
Tip: Get completion help running ‘ctrl-x h’.
zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS}
% bindkey -M menuselect "+" accept-and-menu-complete % ls <tab> +
Makes find(1) useless for many jobs.
% setopt extendedglob % rm ../debianpackage(.) # remove files only % ls -d *(/) # list directories only % ls /etc/*(@) # list symlinks only % ls -l *.(png|jpg|gif) # list pictures only % ls *(*) # list executables only % ls /etc/**/zsh # which directories contain 'zsh'? % ls **/*(-@) # list dangling symlinks ('**' recurses down directory trees) % ls foo*~*bar* # match everything that starts with foo but doesn't contain bar
The e glob qualifier - e.g. to match all files of which file says that they are JPEGs:
% ls *(e:'file $REPLY | grep -q JPEG':)
- (#s) or (#e) for what ^ and $ are in regexps (beginning of line/end of line)
- (#b) or (#m) to enable backreferences
- (#i) to match case insensitive
- (#a) to match approximately (certain errors are ignored, e.g. “(#a1)foo*” matches the string “ofobar”)
Tip: run e.g. `ls *(<tab>` to get help regarding globbing.
Notes:
- \^ := ctrl
- \^[ := esc
Zsh defaults to vi keybindings (‘bindkey -v’) if $VISUAL or $EDITOR contain string ‘vi’. Run ‘bindkey -e’ to get emacs-like keybindings then.
Keybinding | Meaning |
---|---|
ctrl-d | complete + EOF |
ctrl-l | clear screen |
ctrl-w | delete last word |
ctrl-\_ | undo |
tab | complete and take first result |
esc-. | insert last parameter of last typed command (similar to typing !$) |
ctrl-a | begin of line |
ctrl-e | end of line |
alt-’ | quote-line (”) |
alt-? | which-command |
ctrl-k | kill line |
ctrl-u | kill while line (kill-ring) |
ctrl-w | copy last word (kill-ring) |
ctrl-y | yank (insert kill-ring) |
esc-q | push line |
% slash-backward-kill-word() { local WORDCHARS="${WORDCHARS:s@/@}" zle backward-kill-word } % zle -N slash-backward-kill-word % bindkey '\e^?' slash-backward-kill-word % cd /usr/share/doc/mutt/examples/<alt+backspace> Note: configured by default in grml-zshrc, so ready for usage out-of-the-box.
% echo 123 % echo 234 % ls and then: % echo <cursor-up|down> vs. % echo 2<page-up|down>
% <ctrl-r>scp*r
- It’s what readline is for bash (move, delete, copy words/lines/…)
- Basic layout of custom widgets, used like functions:
% foobar() { LBUFFER="foobar $LBUFFER"; } # function % zle -N foobar # declare function as bindable widget % bindkey '^x^s' foobar # bind command to a keybinding
- ctrl-x-z provides help_zle_parse_keybindings in grml-zshrc
% autoload edit-command-line && zle -N edit-command-line % bindkey '\ee' edit-command-line % $SOME_COMMAND_LINE <esc-e>
insert-datestamp() { LBUFFER+=${(%):-'%D{%Y-%m-%d}'}; } zle -N insert-datestamp bindkey '^Ed' insert-datestamp
% insert-last-typed-word() { zle insert-last-word -- 0 -1 }; % zle -N insert-last-typed-word; % bindkey "\em" insert-last-typed-word % mv foobar <esc-m>
% zle -C hist-complete complete-word _generic % zstyle ':completion:hist-complete:*' completer _history % bindkey "^X^X" hist-complete
% autoload -U tetris % tetris
% autoload -U url-quote-magic % zle -N self-insert url-quote-magic
Disclaimer: annoying when using e.g. http://example.org/foo{1,2,3}.tgz
% autoload -U promptinit % promptinit % prompt fire % prompt <tab>
precmd () { RPROMPT="%(?..:()%" }
autoload -Uz vcs_info precmd() { psvar=() vcs_info [[ -n $vcs_info_msg_0_ ]] && psvar[1]="$vcs_info_msg_0_" } PS1="%m%(1v.%F{green}%1v%f.)%# "
% hash -d doc=/usr/share/doc % cd ~doc % hash -d deb=/var/cache/apt/archives % sudo dpkg -i ~deb/foobar*deb
% vared PATH
Supports csh style bang history expansion.
% history # last 16 events % history -E 0 # all history events including date/time information % !23 # Re-execute history command 23 % !! # The last command. % !$ # Last word of the last command. % !-2 # The last but one command. % !-2$ # The last word of the command before the last command. % !#$ # The last word of the current command line. % !#0 # The first word of the current command line. % !?foo # The last command that matches the pattern `foo'. % !?foo?1 # The second word of the last command line that matches `foo'.
…and that’s really just the start. History expansion is extremely versatile and powerful - but also a bit cryptic for the untrained eye. Practice, young padawan, makes perfect. .o( man zshexpn | less -p ‘\^HISTO.*ANSION$’ )
- fc -p/fc -a/fc -P deals with the “history stack”
- “fc -p” clears out the current history and starts with a new one, until you run fc -P, which will restore the old history again
- You can use that to “bind” certain histories to specific directories.
Check your history for most frequently used commands and create aliases/functions for them (AKA top10):
% print -l -- ${(o)history%% *} | uniq -c | sort -nr | head -n 10
% mkdir -p /tmp/linux-2.6.3{8,9}/demo % cd /tmp/linux-2.6.38/demo % cd 38 <tab> % echo foo % ^foo^bar % echo foo_bar % echo !$:s/foo/baz/
% alias -s txt=vim % foobar.txt % alias -s pdf=xpdf % print.pdf
% dchange $DEBIAN_PACKAGE
% cp file /tmp/doesnotexist/<ctrl-xM>
% cdt
See http://michael-prokop.at/blog/2009/05/30/directory-specific-shell-configuration-with-zsh/ Hint: do you remember the fc section? You can combine the directory specific shell configuration with ‘fc -p $file’!
% which cd cd () { if [[ -f ${1} ]] then [[ ! -e ${1:h} ]] && return 1 print "Correcting ${1} to ${1:h}" builtin cd ${1:h} else builtin cd ${1} fi } % cd /etc/fstab
% vim # ... <ctrl-z> % echo foobar % <ctrl-z>
% which sudo-command-line sudo-command-line () { [[ -z $BUFFER ]] && zle up-history if [[ $BUFFER != sudo\ * ]] then BUFFER="sudo $BUFFER" CURSOR=$(( CURSOR+5 )) fi } % gparted /dev/sda <ctrl-o s>
% cd -<tab>
Long version | Short version |
---|---|
for i in $(seq 2 9); do echo $i ; done | for i in {2..9}; echo $i |
ls $(which vim) | ls =vim |
cat bar baz $PIPECHAR sort | sort <b{ar,az} |
ls /usr/share/doc/mutt/examples | ls /u/s/d/m/e<tab> |
gzip -cd foo.gz && less foo | less <(gzip -cd foo.gz) |
ls >file1; ls >file2; ls >file3 | ls >file1 >file2 >file3 |
- | less <file1 <file2 |
- | diff <(sort foo) <(sort bar) |
- | xpdf =(zcat ~doc/grml-docs/zsh/grml-zsh-refcard.pdf.gz) |
- Q: How to I get a listing of all my currently in use options?
Answer:
setopt ksh_option_print && setopt or: printf '%s=%s\n' "${(@kv)options}"
- Q: Why do I get “zsh: command not found:” even though I just installed the program?
Answer: execute:
% rehash
or use completion system as provided by grml-zshrc (completion will rehash automatically).
- Q: What’s this strange word splitting thing?
Answer: see http://zsh.sourceforge.net/FAQ/zshfaq03.html
% var="foo bar" % args() { echo $#; } % args $var 1 % setopt shwordsplit % args $var 2
- Zsh Homepage: http://zsh.sourceforge.net/
- Zsh Wiki: http://zshwiki.org
- Zsh Manpages: man zshall
- Zsh Reference Card: http://www.bash2zsh.com/zsh_refcard/refcard.pdf
- User’s Guide to ZSH: http://zsh.sourceforge.net/Guide/ (old but still interesting)
- Zsh Talk by caphuso: http://ft.bewatermyfriend.org/comp/zshtalk.html
- English Book: http://www.bash2zsh.com/
- German Book: http://zshbuch.org/
- Grml’s Zsh stuff: http://grml.org/zsh/
Thanks to Frank Terbeck for reviewing and his valuable feedback (which isn’t limited to this document :)).
(c) 2011 by Michael Prokop <mika@grml.org>