Zsh: the sheet

Be aware it's not an exhaustive list.

Permalink to heading Basics Basics

Let’s start with some basic concepts.

Permalink to heading Zsh is probably Bash++ Zsh is probably Bash++

Zsh has many of the essential features of bash, ksh, or tcsh, so common syntaxes such as conditional statements, expansions, or assignments will work pretty much the same.

Zsh brings cool additional features you might find helpful, though.

Zsh stands for “Z-Shell” and is meant for Unix systems. It’s an interactive shell that wraps commands you use on the terminal.

Permalink to heading But Zsh is neither bash nor sh But Zsh is neither bash nor sh

Even if Zsh is highly compatible with Bash and sh, we’ll see there are some notable differences. As a result, the following might work:

zsh myscript
sh myscript.zsh

But it’s a bit misleading and you can even get unexpected errors in the worst-case scenario because the default behavior is not the same.

Permalink to heading Installing Zsh Installing Zsh

If Zsh is already available on your system, you can check if zsh is your current shell with:

echo $0

If Zsh is not yet on your system, it’s pretty straightforward to install.

Permalink to heading Best framework Best framework

You can install Oh my zsh, a wonderful framework that eases configuration and provides tons of customizations (e.g., themes, plugins, aliases, etc).

Permalink to heading The agnostic way The agnostic way

You can run the following in the terminal:

sudo apt update && apt install zsh && chsh -s /bin/zsh

The above command install the package and set it as the default shell. If you get an error with /bin/zsh, you can use $(which zsh) alternatively to locate Zsh on the system.

Depending on your Linux distribution, you have to replace sudo apt with the appropriate command. For example, your will use yum -y install for CentOS and pacman -S zsh in Arch Linux.

Permalink to heading Configuration files Configuration files

Zsh uses configuration files (like Bash):

.zshenv  # env vars
.zprofile # external commands
.zshrc # global settings and customizations such as autocompletion, case sensitivity, aliases, etc
.zlogin # commands on shell login
.zlogout # commands on shell exit

Permalink to heading The Shebang The Shebang

It’s the #! at the top of bash files. “Bang” is for the exclamation point “!”:


It’s essential not to forget it, as if you use #!/usr/bin/bash, then the bash interpreter will be used when running ./myscript, regardless of the default shell.

Permalink to heading Executing scripts Executing scripts

zsh myscript.zsh

Alternatively, you can make your script executable and run it:

chmod +x myscript.zsh && ./myscript.zsh

Permalink to heading Zsh can autocorrect Zsh can autocorrect

Zsh is smart enough to detect small typos and suggest corrections for commands:

zsh: correct ‘MISPELLED_COMMAND’ to ‘COMMAND’ [nyae]?

Note that it also works for folder names and files.

It’s pretty cool but you might get annoyed in some cases. You can disable autocorrection like that:


Permalink to heading You don’t have to use cd You don’t have to use cd

You might already know this trick, but in case you don’t, the change directory command is not necessary to browse directories with Zsh. The following command will get you to the desired path:


It’s pretty convenient to quicky move to a nested directory, for example, by drag and droping the folder inside the terminal and pressing enter.

It’s possible thanks to the autocd option.

Permalink to heading You can use keyboard shortcuts in the terminal You can use keyboard shortcuts in the terminal

Like in Bash, you can use shortcuts in the terminal to gain time and energy. For example, Ctrl + U can delete from the cursor to the start of the line, and Ctrl + L will clear the terminal.

Permalink to heading Best documentations Best documentations

Permalink to heading Scripting Scripting

You can use most tricks you learned in Bash and the syntax is the same. There are additional features and important differences, though.

Permalink to heading zsh arrays are 1-indexed zsh arrays are 1-indexed

It’s a major difference with Bash. Arrays do not start at 0 but 1, which might be way less confusing and thus, more intuitive for most users:

MyArray=(All In One)
echo ${#MyArray[2]} # displays 2 in zsh (number of chars of the second element) and 3 in bash (number of chars of the third element)

As a result, the total length is the same as the last index in Zsh.

N.B.: Note that you can modify that default behavior to have 0-indexed arrays, but it’s not something I would recommend as a general rule.

Permalink to heading Zsh is better at globbing Zsh is better at globbing

In Bash, you will likely interact with the filesystem using the find command. For example, to apply a command on all files, you might use:

find . -type f -print | xargs CUSTOM_COMMAND

In Zsh, you can use globbing:


**/ is for recursion and the third wildcard * is to target all elements. (.) will match files.

Permalink to heading All glob qualifiers All glob qualifiers

You can combine many Glob qualifiers like filters to only target the elements you need.

Fnon-empty directory (empty: (/^F))
.plain file
@symbolic link
*executable plain file
r/A/Rreadable by owner/group/world
w/I/Wwritable by owner/group/world
x/E/Xexecutable by owner/group/world
s/S/tsetuid/setgid/sticky bit
fspechas chmod style permissions spec
u:name:owned by user name
g:name:owned by group name
a[Mwhms][-+]naccess time in given units (see below)
m[Mwhms][-+]nmodification time in given units
L[kmp][-+]nsize in given units (see below)
^negate following qualifiers
-toggle following links (first one turns on)
Nwhole pattern expands to empty if no match
Dleading dots may be matched
nsort numbers numerically
o[nLamd]order by given code (see below; may repeat)
O[nLamd]order by reverse of given code
[num]select num-th file in current order
[num1,num2]select num1-th to num2-th file

Source: obda.net

Permalink to heading Common tricks Common tricks

Be careful with qualifiers, as they are not magic. If you scan too large folders, you might get errors. Anyway, it eases common operations for sure.

Permalink to heading List files only List files only
ls **/*(.)
Permalink to heading List directories only List directories only
ls **/*(/)
Permalink to heading Find all files with specific extensions Find all files with specific extensions
ls **/*.md
Permalink to heading Find files larger than 25 megabytes Find files larger than 25 megabytes
ls **/*(.Lm+25)
Permalink to heading List files that have been modified during the last two days List files that have been modified during the last two days
ls **/*(.md-2)
Permalink to heading Change permissions for files owned by a specific user Change permissions for files owned by a specific user
chmod 644 **/*(u:ella:) # provided that ella exists ^^

Permalink to heading Oh my zsh alternatives? Oh my zsh alternatives?

Dev often recommend using Oh my zsh because it’s actively maintained and significantly more friendly to use and configure. It does not mean it’s the only way to customize zsh at all.

You could, for example, open the .zshrc file and write specific lines to customize the prompt without any additional layer. The main caveat is it would be a bit more complicated, perhaps tedious, but many dev would likely stick with the native tools instead of installing frameworks:

# for all options @see https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html
PROMPT='%F{red}%1~%f %# '

# aliases
alias gs='git status'
alias gc='git commit'

I totally respect that approach. I’m not a big fan of alternative frameworks, though. Most tools I’ve tested are not reliable, to me, or lack documentation and concrete examples.

Too much dependencies can be a concern. Besides, you often end up installing and loading tons of plugins while using only a small percentage of all features and aliases.

Permalink to heading Going further Going further

You might need more customizations and bindings.

Permalink to heading Get some inspiration in Kali Linux Get some inspiration in Kali Linux

It’s worth diving into Kali Linux and its default .zshrc file. The pen-testing distribution makes interesting use of advanced features such as bindings and completions:

# ~/.zshrc file for zsh interactive shells.
# see /usr/share/doc/zsh/examples/zshrc for examples

setopt autocd              # change directory just by typing its name
#setopt correct            # auto correct mistakes
setopt interactivecomments # allow comments in interactive mode
setopt magicequalsubst     # enable filename expansion for arguments of the form β€˜anything=expression’
setopt nonomatch           # hide error message if there is no match for the pattern
setopt notify              # report the status of background jobs immediately
setopt numericglobsort     # sort filenames numerically when it makes sense
setopt promptsubst         # enable command substitution in prompt

# enable completion features
autoload -Uz compinit
compinit -d ~/.cache/zcompdump
zstyle ':completion:*:*:*:*:*' menu select
zstyle ':completion:*' auto-description 'specify: %d'
zstyle ':completion:*' completer _expand _complete
zstyle ':completion:*' format 'Completing %d'
zstyle ':completion:*' group-name ''
zstyle ':completion:*' list-colors ''
zstyle ':completion:*' list-prompt %SAt %p: Hit TAB for more, or the character to insert%s
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'
zstyle ':completion:*' rehash true
zstyle ':completion:*' select-prompt %SScrolling active: current selection at %p%s
zstyle ':completion:*' use-compctl false
zstyle ':completion:*' verbose true
zstyle ':completion:*:kill:*' command 'ps -u $USER -o pid,%cpu,tty,cputime,cmd'

# force zsh to show the complete history
alias history="history 0"

# configure `time` format

Source: Kali Linux - .zshrc

Permalink to heading Don’t forget the source command Don’t forget the source command

The source command is necessary if you want to execute scripts inside other scripts or apply modifictions on the current terminal. Every time you modify your .zshrc, run a source ~/.zshrc to load the updated configuration.

Alternatively, you may close the current terminal and open a new one.

Permalink to heading Aliases Aliases

I love using aliases. I think that’s a great way to improve productivity and remove the hassle of remembering and typing the same command lines over and over.

Of course, Zsh allows you to set them. You can directly put them in your .zshrc:

# πŸ₯·πŸ»
alias erase="history -p"

Permalink to heading Source from separate files Source from separate files

It’s fine if you have only a few aliases but I recommend using separate files to keep things organized. I usually set global aliases and specific ones, for example, aliases I use on my free time and aliases for work.

There are several approaches to achieve that but the most straightforward is to source your additional files in the .zshrc:

source "~/.aliases_for_work"
source "~/.aliases_for_fun"

Permalink to heading Suffix aliases Suffix aliases

You can define aliases with the -s option:

alias -s {css,html}=vi

The above line will set vi as editor to open .css and .html files.

Permalink to heading Global aliases Global aliases

Zsh allows you to define global aliases that will be expanded everywhere:

alias -g vid='-v -i --d' # letters are arbitrary here

Then you can use vid directly in the terminal whether it’s at the beginning, in the middle, or at the end of the command line.