The way zsh handles keyboard input can be completely customized.
However, often users run into problems, when their terminal emulator sends charactersequences for special keys, that zsh doesn't know about by default.
Note: If you're here, because your special keys, like HOME, DEL etc. don't work. Use the `terminfo' solution presented on this page. It will work with almost all terminals on all systems, that provide terminfo (even NetBSD has that these days). And without any user-intervention.
If you use several different terminal emulators, it's likely, that you've run into the problem, that pressing a special key like PageDown will just display a tilde instead of doing what it's supposed to.
There is a function described in zshcontrib(1) that reads and stores keydefinitions for special keys, if it recognizes a terminal, it hasn't seen before.
This is a snippet, that shows how it could be used:
autoload zkbd
function zkbd_file() {
[[ -f ~/.zkbd/${TERM}-${VENDOR}-${OSTYPE} ]] && printf '%s' ~/".zkbd/${TERM}-${VENDOR}-${OSTYPE}" && return 0
[[ -f ~/.zkbd/${TERM}-${DISPLAY} ]] && printf '%s' ~/".zkbd/${TERM}-${DISPLAY}" && return 0
return 1
}
[[ ! -d ~/.zkbd ]] && mkdir ~/.zkbd
keyfile=$(zkbd_file)
ret=$?
if [[ ${ret} -ne 0 ]]; then
zkbd
keyfile=$(zkbd_file)
ret=$?
fi
if [[ ${ret} -eq 0 ]] ; then
source "${keyfile}"
else
printf 'Failed to setup keys using zkbd.\n'
fi
unfunction zkbd_file; unset keyfile ret
# setup key accordingly
[[ -n "${key[Home]}" ]] && bindkey "${key[Home]}" beginning-of-line
[[ -n "${key[End]}" ]] && bindkey "${key[End]}" end-of-line
[[ -n "${key[Insert]}" ]] && bindkey "${key[Insert]}" overwrite-mode
[[ -n "${key[Delete]}" ]] && bindkey "${key[Delete]}" delete-char
[[ -n "${key[Up]}" ]] && bindkey "${key[Up]}" up-line-or-history
[[ -n "${key[Down]}" ]] && bindkey "${key[Down]}" down-line-or-history
[[ -n "${key[Left]}" ]] && bindkey "${key[Left]}" backward-char
[[ -n "${key[Right]}" ]] && bindkey "${key[Right]}" forward-char
To get special keys working, you can also try to ask the terminfo database for the actual key sequences; this requires a valid terminfo database for the terminal in question, but works in most cases and requires no user interaction.
The following snippet assigns data from the $terminfo[] array to a $key[] hash, that is compatible to the hash created by zkbd (see above). That makes it possible to switch back and forth between the zkbd solution and the terminfo solution in case something goes wrong.
# create a zkbd compatible hash;
# to add other keys to this hash, see: man 5 terminfo
typeset -A key
key[Home]=${terminfo[khome]}
key[End]=${terminfo[kend]}
key[Insert]=${terminfo[kich1]}
key[Delete]=${terminfo[kdch1]}
key[Up]=${terminfo[kcuu1]}
key[Down]=${terminfo[kcud1]}
key[Left]=${terminfo[kcub1]}
key[Right]=${terminfo[kcuf1]}
key[PageUp]=${terminfo[kpp]}
key[PageDown]=${terminfo[knp]}
# setup key accordingly
[[ -n "${key[Home]}" ]] && bindkey "${key[Home]}" beginning-of-line
[[ -n "${key[End]}" ]] && bindkey "${key[End]}" end-of-line
[[ -n "${key[Insert]}" ]] && bindkey "${key[Insert]}" overwrite-mode
[[ -n "${key[Delete]}" ]] && bindkey "${key[Delete]}" delete-char
[[ -n "${key[Up]}" ]] && bindkey "${key[Up]}" up-line-or-history
[[ -n "${key[Down]}" ]] && bindkey "${key[Down]}" down-line-or-history
[[ -n "${key[Left]}" ]] && bindkey "${key[Left]}" backward-char
[[ -n "${key[Right]}" ]] && bindkey "${key[Right]}" forward-char
# Finally, make sure the terminal is in application mode, when zle is
# active. Only then are the values from $terminfo valid.
function zle-line-init () {
echoti smkx
}
function zle-line-finish () {
echoti rmkx
}
zle -N zle-line-init
zle -N zle-line-finish
If you don't like automation like zkbd provides, you can get the keysequences like this:
In order to see the sequence to give to bindkey for the key you want, use quoted-insert (control-V) and press the key combination. Another way to do this is to `cat > /dev/null` and then press the key combination.
So, in order to get bind something to your keyboard's delete key, you do:
% bindkey '<ctrl-v><delete>' delete-char
That means you *literally* hit control-v followed by delete. The angle-brackets are there for the sake of readability. You DO NOT HAVE TO INCLUDE THEM, in order to get the proper sequence. Also note, that the single-quotes are probably needed, do not leave them out.
You are probably in vi-Mode, because you have set $EDITOR or $VISUAL to something starting with 'vi'. And this question sounds a lot like you do not want that.
The quick fix is to simply switch to emacs-like keybindings via bindkey -e.
However, if you are sure that you want vi-bindings, issue the following commands to enable 'history-incremental-search-backward' in both vi-keytables:
bindkey -M viins '^r' history-incremental-search-backward bindkey -M vicmd '^r' history-incremental-search-backward
If you would like to swap keys inside zsh (like `~), i.e. swap keys X and Y so typing X gives Y and vice versa; check this post from Peter Stevenson:
Link: ZU#9947 (IMO the function is a bit too long for this page)
Here are a few things, that users miss from time to time.
export WORDCHARS=''
If you prefer, you can make WORDCHARS local to the definition of say, backword-word, so that kill-word still deletes an entire path. To do this, you need to define a widget function. For example, for backward-word, I use this:
tcsh-backward-word() {
local WORDCHARS="${WORDCHARS:s@/@}"
zle backward-word
}
zle -N tcsh-backward-word
PiyoPiyo: This is what I use in my own setup:
# by default: export WORDCHARS='*?_-.[]~=/&;!#$%^(){}<>'
# we take out the slash, period, angle brackets, dash here.
export WORDCHARS='*?_[]~=&;!#$%^(){}'
bindkey '^[[5D' emacs-backward-word bindkey '^[[5C' emacs-forward-word
bindkey ';5D' emacs-backward-word bindkey ';5C' emacs-forward-word
bindkey "\e\e[D" backward-word bindkey "\e\e[C" forward-word
a terminal has no knowledge of a Ctrl-Arrow keypress, BUT if you use a terminal emulator (like xterm or rxvt under X11) you can assign an X keyboard event to a string sequence like '^[[5D' that you then use in bindkey. Here is a line I have in my .Xresources
XTerm*vt100.Translations: #override\n\
Ctrl <KeyPress> Left : string("\033[90~") \n\
Ctrl <KeyPress> Right : string("\033[91~") \n
bindkey "^J" self-insert
to my .zshrc. Thus I can type multiline command lines, and still be able to move the cursor up/down between the lines while editing. (This also works quite well with Ctrl-A and Ctrl-E, which if they're at the beginning/end of a line jumps to the beginning/end of the previous/next line.) – Zrajm