Why Did Apple Switch to ZSH?

Why did Apple switch to use? In the recent years Apple has moved from Mac to a “Newton” platform. The switch from Mac to iOS devices was driven primarily by Apple’s desire to provide customers with a new device that started life on an apple product. Apple’s desire to remain a technology leader also drove their decision to develop an intuitive user interface. Apple would essentially change everything about the Mac OS X system, including its underlying software, in order to place it on a non-compatible hardware. The result is that Mac OS X now supports only one single input method: zsh.

Why did Apple switch to zsh

Apple is heavy on simplifying their products and command line interface has been another area where they have strived for simplicity. In fact, the new features that the company has introduced into the command line (the Terminal) are so simple that they may be used by children. Apple has leveraged the power of Vim and bash by providing a single command line session that can run multiple commands. The result is that you can now run multiple tasks concurrently. In essence you can do your typing in the terminal and then simultaneously perform other tasks in your Mac environment.

Not long ago, Apple switched to using Vim as the default terminal for OS X. When Apple switched to using Vim, a lot of people wondered why, since vi is so much more mature and widely used. Apple has leveraged the power of Vim by adding support for additional languages. So if you are a programmer or developer you can utilize your Vim skills to write some additional programs.

Apple switched to using bash as the default shell for OS X.

This was done not only as a marketing strategy but it also made it much easier for system administrators to manage the different users that were sharing the same system. In fact, many third-party companies even develop interactive shells for OS X based on bash.

Apple has leveraged the power of bash by adding support for additional languages. So if you are a programmer or developer you can utilize your Vim skills to write some additional programs. For example, you can find a lot of companies that offer source code editing applications for Linux systems. These applications work just the same way as those that you would find in a Windows environment and can be run using a simple command line interface.

Nowadays, most people use Unix-like platforms for web browsing and developing. Even those who use Macs still use a user account similar to bash. In order to switch to a user account, the user must log in as root. By logging in as root, one can execute different commands. One such command is “ksh”, which enables you to switch to your own user account.

Not all shells are equal.

One of the primary reasons why Apple switched to zsh is because it is closer to the functionality of the standard bash. If you are familiar with the standard bash, you will be glad to know that there is an Apple ZSH prompts which looks similar to the one you will find in Microsoft Outlook. The only difference is that you do not need to type anything after the @ sign. To switch to your own user account, enter a command followed by a colon and then a space. To return to the main menu, enter a command followed by a colon.

Although many developers claim that they have the “toughest” shells available, only few of them can provide you with a robust computer program. Apple has designed its own shells which are able to run all kinds of useful programs such as Excel, Safari, and Firefox among others. With these programs, you can literally enjoy working on your computer. Therefore, you should consider using Apple’s default shell because it is the easiest to work with.

Keybindings

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.

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.

See EmacsAndViKeys for keybinding nirvana.
zkbd – special keys solution

If you use several different terminal emulators, it’s likely, that you’ve run into the problem of neil, 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[Backspace]” ]] && bindkey — “$key[Backspace]” backward-delete-char
[[ -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

reading $terminfo[]

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 -g -A key

key[Home]=”$terminfo[khome]”
key[End]=”$terminfo[kend]”
key[Insert]=”$terminfo[kich1]”
key[Backspace]=”$terminfo[kbs]”
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[Backspace]” ]] && bindkey — “$key[Backspace]” backward-delete-char
[[ -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.
if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
function zle-line-init () {
echoti smkx
}
function zle-line-finish () {
echoti rmkx
}
zle -N zle-line-init
zle -N zle-line-finish
fi

CTRL-V – manually find out esc.-sequences

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 ” 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.
Why isn’t Control-R working (anymore)?

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

Swapping Keys

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)
Most Wanted

Here are a few things, that users miss from time to time.

One of the things I most missed about bash when first switching to Zsh was that M-b and M-f (backward-word and forward-word) would jump over an entire /path/location, rather than to each word separated by a ‘/’. To make the behavior more like bash, you can evaluate this command:

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:[email protected]/@}”
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=’*?_[]~=&;!#$%^(){}’

A question about binding keys: The one emacs keybinging I really miss in zsh is Ctrl-LeftArrowKey? and Ctrl-RightArrowKey? as backward-word and forward-word. I can’t seem to find out if it is possible to duplicate these keybindings in zsh. Is the terminal able to distinguish between LeftArrowKey? and Ctrl-LeftArrowKey?? It doesn’t seem to be from my investigations. Has anybody done this? Can it be done? โ€“ Merc
This appears to work:

bindkey ‘^[[5D’ emacs-backward-word
bindkey ‘^[[5C’ emacs-forward-word

This works: โ€“ czo

My xterm (KDE Konsole) was showing “;5D” and “;5C”, so I just used them instead:

bindkey ‘;5D’ emacs-backward-word
bindkey ‘;5C’ emacs-forward-word

on my urxvt it works with -/-

bindkey “\e\e[D” backward-word
bindkey “\e\e[C” forward-word

โ€“ phelix

If you see such a key sequence, I would try using it before a custom remapping as described below.
โ€“mattflaschen

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 Left : string(“\033[90~”) \n\
Ctrl Right : string(“\033[91~”) \n

I have remapped shift-enter (using loadkeys for the console, and xmodmap in X) to produce Ctrl-J and added

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

Introduction

Zsh is a shell for Un*x systems that has been around since 1990 and is more sophisticated and configurable than most shells out there. There are lots of features, such as:

Context-based completion of many functions (like remote filename completion in ssh, makefile targets, configure scripts, and much more), which is highly configurable.
A powerful programming language with complex pattern matching mechanism (a pattern like **/*(^/) matches all (non-hidden) files in all subdirectories, but not directories themselves)
Lots of CLI (Command Line Interface) options and bindkeys
Loadable module support for adding builtin commands

Zsh has a very good User Guide. This wiki doesn’t aim to take its place, but to provide an accessible outline of all the features of Zsh (with some nifty specifics!), provide examples of use, and foster a Zsh community.