Zsh specific tips

See also the shell code page for more general tips.

Make sure to also read through the example configuration for any further comments.

Guarding against wrong invocation

If you use (say) any Zsh-specific coding in shell scripts, at the very least make sure to put #!/usr/bin/zsh or similar into the header so the right interpreter is run when the file is executed. Even so, a direct sh invocation might be inevitable at some point and so care should be taken that things won't run amok if the wrong interpreter is used. Here's some example code for this that should work in Bash, Dash and Zsh:

INTERPRETER=`readlink /proc/$$/exe`
echo $INTERPRETER | grep '/zsh' > /dev/null

if [ $? -eq 1 ]
then
  echo "Using wrong interpreter! ($INTERPRETER)"
  exit 1
fi

File matching tricks

To match recursively, use **/ as a starting point. Example:

% ls -xd **/*
bar  foo  foobar  foo/bar

To match all non-directories, use *(.) . Example:

% ls -xd *(.) 
foobar
% ls -xd **/*(.)
foobar  foo/bar

To match all directories, use */ . Example:

% ls -xd */
bar/  foo/
% ls -xd **/ 
bar/  bar/foo/  foo/

"Read-only" tab completion

If you want to have tab completion without the command line being altered, Control-D works for me. It may also be useful for completing things with certain types of characters in them. Example:

% touch \*
% ls -x
*

Pressing Tab after:

% ls *

in this case would get you:

% ls \*

But Control-D would get you this:

% ls *
Completing files
\*

Disable hashing

(Among the reasons for being very explicit in this section is to make it possible to generalize the lessons learned here. See the Solution subsection for a quick answer.)

Hashing (or caching of all directories in your PATH variable) is enabled by default. This is usually not a problem, but when new executables get put in place one would have to do something like:

which $NAME
$NAME

or:

/usr/bin/$NAME

or:

PATH="$PATH"

or:

hash -r
$NAME

etc.

Sure, one could setup a suitable keybind to make rehashing very easy, but it just feels wrong for me, coming from Bash and all.

In theory completion might be slower if the equivalent of "hash -r" is run every time, but it would be worth it, plus I don't know if there will actually be any practical drawbacks for me in the end with this.

Solution

After much digging (likely not very time efficient for myself in the end, but I was annoyed and I couldn't find any simple suggestions that worked) I came up with:

function complete-no-hash
{
  hash -r # rehash automatically in order to find new executables (if any)
  zle expand-or-complete # fortunately we don't need to forward any arguments here or it would get more complex
}

zle -N complete complete-no-hash
bindkey '^I' complete

Make sure that the widget (or "command" from a bindkey perspective) you specify for zle (here: "complete") does not collide with any existing definitions. See below for debugging suggestions.

Failed attempts

Other things I tried (and that didn't seem to help at all):

zstyle ':acceptline:*' rehash true # not sure what this does

and:

zstyle ':completion:*' use-cache false # sounds good but didn't do anything for me

and:

  • Modifying the HASHCMDS (HASHALL), HASHDIRS and HASHLISTALL options and their opposites, but I'm not sure these are the ones needed here. By the way, they are commonly referred to in various other forms (note that both case as well the presence of underscores seems to ignored by Zsh):
    • HASH_CMDS (HASH_ALL), HASH_DIRS and HASH_LIST_ALL
    • NOHASHCMDS (NOHASHALL), NOHASHDIRS and NOHASHLISTALL
    • NO_HASHLISTALL
    • NO_HASHCMDS (NO_HASHALL), NO_HASHDIRS and NO_HASHLIST_ALL
    • NO_HASH_CMDS (NO_HASH_ALL), NO_HASH_DIRS and NO_HASH_LIST_ALL

Note that zle -C may not work as intended. I got errors like:

zle: widgets can only be called when ZLE is active

or: (when adding autoload -U zle )

zle: function definition file not found

I stopped trying with zle -C shortly after that. Possibly there is an answer somewhere in the zshzle(1) or zshcompwid(1) man pages, or zshall(1) if you know what you're looking for but not in which section.

Troubleshooting

For debugging, you may also need:

zle -la

and:

bindkey -l

and:

bindkey -M main # default

(and so on) and:

hash

If you want to play around with setopt , without arguments to see all settings that have non-default values. Use tab completion to find all settings you can use (or see zshoptions(1)). Similarly use unsetopt (or go with "setopt no<Tab>") for convenient disabling of any option. Underscores (that is, '_') would seem to be ignored by setopt / unsetopt , but the tab completions will adjust accordingly.

If you have really strange errors, you can also try deleting ~/.zcompdump , which contains things like a mapping of existing completion functions, and keybindings.

Dereference symlinks

To make sure that cd and other commands dereference symlinks, add:

setopt CHASE_LINKS

to ~/.zshrc .

Treat hash characters as comment delimiters

setopt INTERACTIVE_COMMENTS

This will make sure that commands like:

echo a # && echo b

do not result in:

a #
b

but rather:

a

History search

Vincent Danen explains how to gain access to forward history search (among other things) in Zsh. While one could change the terminal settings to get access to Ctrl-S, the default keybinding, I'd prefer changing the shell options instead. Here's what I have in my .zshrc:

bindkey "^[r" history-incremental-search-forward

That is, Alt-r (lower case). It happened to be the closest match to Ctrl-r I could find that was not occupied.

zshzle(1) might give some more clues as well.

Example configuration

~/.zshrc : (Note that this is not necessarily updated to reflect all tips above or elsewhere on this site. It is not an identical copy of my own configuration either.)

# The following lines were added by compinstall

zstyle ':completion:*' format 'Completing %d'
  # Useful to see the difference between something being uncompletable (no candidate(s) matching the criteria) and
  #   a slow completion such as that of package names.

autoload -Uz compinit
compinit
# End of lines added by compinstall
# Lines configured by zsh-newuser-install
HISTFILE=~/.histfile
HISTSIZE=1000
SAVEHIST=1000
setopt autocd notify
unsetopt beep
bindkey -e
# End of lines configured by zsh-newuser-install

### OPTIONS ETC.
setopt CHASE_LINKS
setopt INTERACTIVE_COMMENTS

# Enable colour support for ls
if [ "$TERM" != "dumb" ] && [ -x /usr/bin/dircolors ]
then
  eval "`dircolors -b`" # This is actually code for Bash, but it seems to work well enough
fi

# A few lines here based on http://aperiodic.net/phil/prompt/prompt.txt (http://aperiodic.net/phil/prompt/)
autoload colors zsh/terminfo

if [[ "$terminfo[colors]" -ge 8 ]]
then
  colors

  for color in RED GREEN YELLOW BLUE MAGENTA CYAN WHITE
  do
    eval PR_$color='%{$terminfo[bold]$fg[${(L)color}]%}'
    eval PR_LIGHT_$color='%{$fg[${(L)color}]%}'
  done
  PR_NO_COLOUR="%{$terminfo[sgr0]%}"

  PROMPT="${PR_GREEN}%m${PR_NO_COLOUR}:${PR_BLUE}%~${PR_NO_COLOUR} %# "
else
  PROMPT="%m:%~ %# "
fi

function complete-no-hash
{
  hash -r # rehash in order to find new executables (if any)
  zle expand-or-complete
}

zle -N complete complete-no-hash
bindkey '^I' complete
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License