Z Shell#


Table of Contents#


zsh --version

Output: zsh 5.9 (x86_64-apple-darwin23.0)

echo $ZSH_VERSION

Output: 5.9


man zsh
man zshexpn
man zshmisc    # conditional expressions
man zshmodules
zsh --help     # display options

Startup config files#

~/.zshrc “runtime configuration”

Startup files

/etc/zlogin   # run for login shells
  ~/.zlogin   # run for login shells
/etc/zprofile # run for login shells
  ~/.zprofile # run for login shells
/etc/zshenv   # run for every shell
  ~/.zshenv   # run for every shell (usually)
/etc/zshrc    # run for interactive shells
  ~/.zshrc    # run for interactive shells

Shutdown files

/etc/zlogout  # run for login shells
  ~/.zlogout  # run for login shells
zsh -f # start a new interactive shell and set the `NO_RCS` option so that no zsh startup files are run other than `/etc/zshenv`

Jobs#

https://zsh.sourceforge.io/Doc/Release/Jobs-_0026-Signals.html

Jobs

  • & operator - send a task to the background

  • jobs - list tasks that are currently running in the background

  • fg n - bring task n back to the foreground from the background (bash)

  • %n or fg %n - bring task n back to the foreground from the background (zsh)

    • fg implies fg %1

  • % or fg %

  • %- or fg %- bring the previous task to the foreground from the background (zsh)

  • %+ or %% or fg %+ or fg %% bring the current task to the foreground from the background (zsh)

  • Ctrl-Z - suspend a process that is currently running in the foreground and then send it back to the background

  • bg - continue running all suspended background processes

  • bg n - continue running suspended background processes n (bash)

  • bg %n - continue running suspended background processes n (zsh)

  • bg % ?

  • bg %- ?

  • bg %+ ?

ping 192.168.10.56 > ping.log &  # send stdout to the background (stderr prints to the screen)
ping 192.168.10.56 &> ping.log & # send both stdout and stderr to the background
ping google.com > /dev/null &
ping amazon.com > /dev/null &
ping oreilly.com > /dev/null &
jobs -p
[1]    running    ping google.com > /dev/null
[2]  - running    ping amazon.com > /dev/null
[3]  + running    ping oreilly.com > /dev/null
%3

[3]  - 4048 running    ping oreilly.com > /dev/null

Ctrl-Z

[3]  + 4048 suspended  ping oreilly.com > /dev/null

jobs -p
[1]    running    ping google.com > /dev/null
[2]  - running    ping amazon.com > /dev/null
[3]  + suspended  ping oreilly.com > /dev/null
%2

[2]  - 4037 running    ping amazon.com > /dev/null

Ctrl-Z

[2]  + 4037 suspended  ping amazon.com > /dev/null

jobs -p
[1]    running    ping google.com > /dev/null
[2]  + suspended  ping amazon.com > /dev/null
[3]  - suspended  ping oreilly.com > /dev/null
bg %3

[3]  - 4048 continued  ping oreilly.com > /dev/null

jobs -p
[1]    running    ping google.com > /dev/null
[2]  + suspended  ping amazon.com > /dev/null
[3]  - running    ping oreilly.com > /dev/null

Parameters (Variables)#

Normal parameters are also called scalars because they have just one word in them.

# there are no spaces around parameter assignment
foo=bar                     # stores the value `bar` in the parameter `foo`
foo='This is a parameter.'  # stores the value `'This is a parameter.'` in the parameter `foo`
foo ='This is a parameter.' # attempts to invoke the non-existent command `foo` with argument `='This is a parameter.'`
foo= 'This is a parameter.' # stores the empty string in the parameter `foo`

Parameter Expansion (or Substitution)

foo=bar
print $foo               # bar
print '$foo is "'$foo'"' # $foo is "This is a parameter."

Modifiers

  • pattern matching

  • partial replacement of modified parts of the original string


Arrays#

Arrays are a special kind of parameter that have more than one word in them.

arr=(one two three four)     # original command
print $arr                   # one two three four
print ${arr}                 # one two three four
print ${arr[2]}              # two
print ${arr[2,-1]}           # two three four
print ${arr[2,-1][1]}        # two
print ${arr[2,-1][1][2,-1]}  # wo

print $#arr                  # 4
print $arr                   # one two three four
# sets the second element to the empty string but does not remove the second element
arr[2]=
print $#arr                  # 4
print $arr                   # one three four
# replaces the second element with an array of length zero, which removes it
arr[2]=()
print $#arr                  # 3
print $arr                   # one three four

arr=(what kind of fool am i) # original command
arr[2]=species
print $arr                   # what species of fool am i
arr[2]=(a piece)
print $arr                   # what a piece of fool am i
arr[-3,-1]=(work is a man)
print $arr                   # what a piece of work is a man

“You need typeset or equivalent if you want to the array to be local to a function. The neat way is typeset -a, which creates an empty array, but as long as you assign to the array before trying to use it any old typeset will do.”

Subscripts have flags for special purposes.

  • r “reverse subscripting”: search through arrays and match and return values or the empty string (starting from the left)

  • R “reverse subscripting”: search through arrays and match and return values or the empty string (starting from the right)

  • i return the index matched (starting from the left)

  • I return the index matched (starting from the right)

arr=(se vuol ballare signor contino)
print ${arr[(r)s*]}                  # se
print ${arr[(R)s*]}                  # signor

arr=(some words); args () { print $#; }
args ${arr[(r)s*]}                   # 1
# the unquoted empty string is dropped
args ${arr[(r)X*]}                   # 0
# the quoted empty string is passed as a single empty argument
args "${arr[(r)X*]}"                 # 1

arr=(se vuol venire nella mia scuola)
print ${arr[(i)v*]}                  # 2
print ${arr[(I)v*]}                  # 3

-- in a command line says that there are no more options that begin with a -

print -hello    # print: bad option: -h
print -- -hello # -hello
print - -hello  # -hello (non-standard)

“The shell is “eight-bit clean” which means that you can have any of he 256 possible characters anywhere in your string. For example, $’foo\000bar’ has an embedded ASCII NUL in it (that’s not a misprint–officially, ASCII non-printing characters have two- or three-letter abbreviations). Usually this terminates a string, but the shell works around this when you are using it internally; when you try and pass it as an argument to an external programme, however, all bets are off. Almost certainly the first NUL in that case will cause the programme to think the string is finished, because no information about the length of arguments is passed down and there’s nothing the shell can do about it. Below, the shell’s echo knows about the shell’s 8-bit conventions, and prints out the NUL, which the terminal doesn’t show, then the remainder of the string. The external version of echo didn’t know any better than to stop when it reached the NUL.”

echo $'foo\000bar'

Output: foobar

/bin/echo $'foo\000bar'

Output: foo

echo is a shell builtin.

type echo

Output: echo is a shell builtin

/bin/echo is an external program.

type /bin/echo

Output: /bin/echo is /bin/echo


Sequences and Brace Expansion#

  • echo {0..10}

  • echo {00..10} (Bash v4+)

  • echo {10..0}

  • echo {10..0..2} (Bash v4+)

  • echo {a..z} vs echo {A..z} vs echo {a..z} {A..Z} vs echo {a..z}{A..Z}

  • echo {z..a..2} (Bash v4+)


Quoting#

“Using quotes is an important part of controlling the effects of the shell’s various substitutions.

Backslashes are trivial: you can quote any character whatsoever from the shell with a backslash even if it didn’t mean anything unquoted. You can take any old string at all, whatever it has in it–random collections of quotes, backslashes, unprintable characters–quote every single character with a backslash, and the shell will treat it as a plain string”

print \h\e\l\l\o\ \w\o\r\l\d\!\ \\\n

Output: hello world!\n

read string

Enter the following: This is a *string* with various `special' characters

echo $string

Output: This is a *string* with various `special' characters

print -r -- ${(q)string}

Output: This\ is\ a\ \*string\*\ with\ various\ \`special\'\ characters

print -r -- ${(qq)string}

Output: 'This is a *string* with various `special'\'' characters'

print -r -- ${(qqq)string}

Output: "This is a *string* with various \`special' characters"

print -r -- ${(qqqq)string}

Output: $'This is a *string* with various `special\' characters'

eval print -r -- ${(q)string}

Output: This is a *string* with various `special' characters

echo "print 'a quoted string' and\ another\ argument" > file
read -r line < file
for word in ${(z)line}; do
  print -r "quoted:    $word"
  print -r "unquoted:  ${(Q)word}"
  done

Output:

quoted:    print
unquoted:  print
quoted:    'a quoted string'
unquoted:  a quoted string
quoted:    and\ another\ argument
unquoted:  and another argument

Option RC_QUOTES converts two single quotes into one single quote inside single quotes.

unsetopt rcquotes
print -r 'A ''quoted'' string'

Output: A quoted string

setopt rcquotes
print -r 'A ''quoted'' string'

Output: A 'quoted' string


POSIX quotes begin with $' and end with '.

cat <<<$'Line\tone\nLine\ttwo'

is equivalent to

print 'Line\tone\nLine\ttwo'

Output:

Line    one
Line    two

Without the initial $:

cat <<<'Line\tone\nLine\ttwo'

Output: Line\tone\nLine\ttwo


Double quotes

  • parameter expansion

  • command substitution

  • arithmetic subsitition

  • NO process substitution

  • NO brace, initial tilde, equal sign expansion

  • NO patterns

“Word splitting is usually suppressed except in the case of parameter substitution which allows you to specify that normal word-splitting will occur as well as other forms of substitution using the flag (@).”

arr=(one two)
print $(echo foo bar) $arr

Output: foo bar one two

print -l $(echo foo bar) $arr

Output:

foo
bar
one
two
print -l "$(echo foo bar) $arr"

Output: foo bar one two

print -l "${arr[@]}" "${(@)arr}"

Output:

one
two
one
two
args () { print $#; } # report the number of arguments
echo "Words on line one\nWords on line two\n" > file
cat file

Output:

Words on line one
Words on line two
args $(<file)

Output: 8

args "$(<file)"

Output: 1

args "${(f)$(<file)}"

Output: 2


Flags

  • @

  • f split the result of the expansion one word per line

  • j:,: replace with comma

  • q quote with backslashes

  • qq quote with single quotes

  • qqq quote with double quotes

  • qqqq quote with POSIX quotes

  • Q unquote

  • z split a line into an array


History Expansion#

View the command-line history list.

history

View the n most recent command lines in the command-line history list.

history -n

View the history characters.

echo $HISTCHARS

!^#

View the history list’s capacity.

echo $HISTSIZE

50000

echo $SAVEHIST

10000

echo $HISTFILE

~/.zsh_history

History Characters

  • ! history character

  • ^ modification character

  • # comment character

A history expansion begins with the first character of the histchars parameter !. The first character is followed by an optional event designator and then an optional word designator; if neither of these designators is present then no history expansion occurs.

! <event designator> : <word designator> : <modifier>

An event designator is a reference to a command-line entry in the history list. ! is the event designator for the previous command. A word designator indicates which word or words of a given command line are to be included in a history reference. The event designator and the word designator are separated by a colon : but may be omitted if the word designator begins with ^, $, *, -, %.

does work

  • echo 'hello!!'

  • echo "hello\!\!"

does not work

  • echo hello!!

  • echo "hello!!"

Event Designators

  • !! refer to the previous command

  • !# refer to the current command-line typed so far

  • !n refer to command-line n

  • !-n refer to the current command-line minus n

  • !str refer to the most recent command starting with str

  • !?str[?] refer to the most recent command containing str; the trailing ? is necessary if this reference is to be followed by a modifier

  • !{...} insulate a history reference from adjacent characters

echo one two three # previous command
!!                 # echo one two three
echo !!            # echo echo one two three
echo !#            # echo echo
!-1                # echo one two three
!echo              # echo one two three

Word Designators

  • 0 the first input word (i.e., the command)

  • n the nth argument

  • ^ the first argument

  • $ the last argument

  • x-y a range of words (x defaults to 0)

  • * all the arguments; or a null value if there are none

  • x* abbreviates the range x-$

  • x- like x* without the final word $

echo one two three # original command
!!0                # echo
!!^                # one
!!1                # one
!!1*               # one two three
!!1-               # one two
!!2-3              # two three
!!$                # three
echo !?tr?:*       # echo tree . -aL 3

Modifiers#

Modifiers are used in

  • history expansion

  • parameter substitution

  • file name generation (globbing)

Modifiers

  • a transform a file name into an absolute path

  • A transform a file name into an absolute path and resolve symlinks

  • hn keep the head of the path (i.e., everything except the last component of the path); use n to keep the first n components of the path (the root directory / is the first component of an absolute path)

  • tn keep the tail of the path (i.e., the last component of the path); use n to keep the last n components of the path

  • r remove the file name extension

  • l convert everything to lowercase

  • u convert everything to uppercase

Substitution Modifiers

  • s/<string>/<replacement>/<flag> substitute <string> with <replacement> using an optional <flag>

  • global subsitution

    • gs/<string>/<replacement>/

    • s/<string>/<replacement>/:g

  • the metacharacter & expands to <string>; to use the literal character & escape the metacharacter \&

ls script.sh                              # original command
!!^:a                                     # /home/user/workspace/project/script.sh

ls /home/user/workspace/project/script.sh # original command
!!^:h                                     # /home/user/workspace/project
!!^:h1                                    # /
!!^:h2                                    # /home
!!^:t                                     # script.sh
!!^:t1                                    # script.sh
!!^:t2                                    # project/script.sh
!!^:t:r                                   # script
!!^:t:r:u                                 # SCRIPT

echo hello hello bonjour                  # original command
!!:s/hello/bonjour                        # echo bonjour hello bonjour
!!:gs/hello/bonjour                       # echo bonjour bonjour bonjour

param=~/file
print $param                              # /Users/<user>/file
print !-1:t                               # file
print ${param:t}                          # file

print foo                                 # foo (enter)
^foo^bar                                  # print bar (enter) (ctrl-c)
param='this sentence contains a foo.'     #
print ${param:&}                          # print this\ sentence\ contains\ a\ bar. (tab)

print a sentence with a /real/live/bogus/path in it.
print !!:t                                # path in it.

arr=('a bar of chocolate' 'a bar of barflies' 'a barrier of barns')
print ${arr:s/bar/car}                    # a car of chocolate a car of barflies a carrier of barns
print ${arr:gs/bar/car}                   # a car of chocolate a car of carflies a carrier of carns

Modifiers in file name generation (i.e., globbing)

touch {parser,lexer,input,output}.c
print *.c                            # parser.c lexer.c input.c output.c
print *.c(:r)                        # parser lexer input output

mkdir -p stuff/subdir && touch stuff/{one,two}file.c
print stuff/*                        # stuff/onefile.c stuff/twofile.c stuff/subdir
print stuff/*(.)                     # stuff/onefile.c stuff/twofile.c
print stuff/*(.:r:t)                 # onefile twofile

Globbing (File Name Generation)#

Globbing Qualifiers

  • . regular files (no directories nor special files)

File name modification is the only form of globing where the result is no longer a file name and is always performed at the end after all normal file name generation.


Arrays#

Indexed Arrays#

arr=(a b c)
print ${(t)arr} # "array"

Associative Arrays#

declare -A aarr  # declare an associative array
typeset -A aarr  # declare an associative array

print ${(t)aarr} # "association"

aarr[a]=1
aarr[b]=2
aarr[c]=3

aarr=([c]=3 [d]=4 [e]=5)  # overwrite pre-existing associative array
aarr+=([c]=3 [d]=4 [e]=5) # modify    pre-existing associative array
aarr=()                   # clear     pre-existing associative array

print $aarr               # "1 2 3 4 5"
print ${(v)aarr}          # "1 2 3 4 5"
print ${(k)aarr}          # "a b c d e"
print ${(kv)aarr}         # "a 1 b 2 c 3 d 4 e 5"
print $aarr[a]            # "1"

for k v in ${(kv)aarr}; do
  print "$k -> $v"
done
# "a -> 1"
# "b -> 2"
# "c -> 3"
# "d -> 4"
# "e -> 5"

Login vs Non-login Shell#

Include the following function in file $HOME/.zshrc.

# determine whether a shell is a login shell or a non-login shell
sl () {
  if [[ -o login ]]; then
    print yes
  else
    print no
  fi
}

Log into the machine and check the shell type.

echo $0

-zsh

sl

yes

Start a new interactive non-login shell.

zsh
echo $0

zsh

sl

no

Start a new interactive login shell.

zsh -l
echo $0

zsh

sl

yes

Invoke a non-interactive shell on a file.

zsh filename

Environment Variables#

  • CPUTYPE

  • HISTCHARS

  • HISTFILE

  • HISTSIZE

  • EDITOR

  • LESSCLOSE

  • LESSOPEN

  • MAIL

  • MAIL_WARNING

  • MAILCHECK

  • MANPATH

  • PAGER

  • PROMPT

  • PS1

  • PS2

  • PS3

  • PS4

  • SAVEHIST

  • SHELL

  • USER

  • VISUAL

  • ZDOTDIR

CPUTYPE

echo $CPUTYPE

Output: arm64

PAGER

echo $PAGER

Output: less

SHELL

echo $SHELL

Output: /bin/zsh

which zsh

Output: /bin/zsh

echo $0

Output: -zsh

ZDOTDIR

If the user’s zsh startup files are located in the standard location–the user’s home folder–then the following command doesn’t display anything.

print $ZDOTDIR

Shell Options#

Each option describes one particular shell behavior. Options can be written uppercase or lowercase with as many or as few underscores as you like. An option with “no” in front just means the opposite of the option without (NOMATCH and NOTIFY have “no” as a part of their base name).

  • (NO_)APPEND_HISTORY

  • (NO_)AUTO_CD

  • (NO_)BAD_PATTERN

  • (NO_)BANG_HIST

  • (NO_)BARE_GLOB_QUAL turn off zsh globbing behavior: file name modifiers

  • (NO_)BEEP

  • (NO_)BG_NICE

  • (NO_)BSD_ECHO

  • (NO_)CD_ABLE_VARS

  • (NO_)CORRECT

  • (NO_)CORRECT_ALL

  • (NO_)CSH_JUNKIE_HISTORY

  • (NO_)CSH_JUNKIE_LOOPS

  • (NO_)CSH_JUNKIE_QUOTES

  • (NO_)CSH_NULL_GLOB

  • (NO_)EXTENDED_GLOB

  • (NO_)EXTENDED_HISTORY

  • (NO_)FUNCTION_ARG_ZERO

  • (NO_)GLOB_SUBST

  • (NO_)HIST_ALLOW_CLOBBER

  • (NO_)HIST_BEEP

  • (NO_)HIST_EXPIRE_DUPS_FIRST

  • (NO_)HIST_FIND_NO_DUPS

  • (NO_)HIST_IGNORE_ALL_DUPS

  • (NO_)HIST_IGNORE_DUPS

  • (NO_)HIST_IGNORE_SPACE

  • (NO_)HIST_NO_FUNCTIONS

  • (NO_)HIST_NO_STORE

  • (NO_)HIST_REDUCE_BLANKS

  • (NO_)HIST_SAVE_NO_DUPS

  • (NO_)HIST_VERIFY if set, then after the substitution the line appears again with the changes instead of being immediately printed and executed

  • (NO_)HUP “hang up”

  • (NO_)IGNORE_BRACES

  • (NO_)INC_APPEND_HISTORY

  • (NO_)INTERACTIVE_COMMENTS

  • (NO_)KSH_ARRAYS make arrays behave more like Korn Shell arrays (braces are necessary; arrays are zero-indexed)

  • (NO_)KSH_AUTOLOAD

  • (NO_)KSH_GLOB turn on ksh globbing behavior

  • (NO_)KSH_OPTION_PRINT

  • (NO_)LOCAL_OPTIONS

  • (NO_)LOCAL_TRAPS

  • (NO_)MONITOR

  • (NO_)MULTIOS

  • (NO_)NOMATCH

  • (NO_)NOTIFY

  • (NO_)POSIX_BUILTINS

  • (NO_)PROMPT_BANG

  • (NO_)PROMPT_PERCENT

  • (NO_)PROMPT_SUBST

  • (NO_)RCS

  • (NO_)RM_STAR_SILENT

  • (NO_)SH_FILE_EXPANSION

  • (NO_)SH_GLOB turn off zsh globbing behavior

  • (NO_)SH_OPTION_LETTERS

  • (NO_)SH_WORD_SPLIT treat parameter assignment like bash

  • (NO_)SHARE_HISTORY

  • (NO_)SINGLE_LINE_ZLE

  • (NO_)ZLE

set -o | sort # view a full list of set and unset options
set +o | sort # view a full list of set and unset options
setopt        # view set options
unsetopt      # view unset options
set           # view positional parameters, zsh's way of passing arguments to scripts and functions

Resources#

[ h ] Z Shell

  • [ h ] An Introduction to the Z Shell.

  • [ h ] Falstad, Paul. (2022). The Z Shell Manual.

  • [ h ] Stephenson, Peter. (2003). A User’s Guide to the Z-Shell.

[ h ][ d ][ g ][ w ] zsh

[ h ][ d ][ g ][ w ] Oh My Zsh

  • [ g ] Plugins

    • [ g ] 1password

    • [ g ] brew

    • [ g ] dotenv

    • [ g ] gh

    • [ g ] git

    • [ g ] git-lfs

    • [ g ] gitignore

    • [ g ] history

    • [ g ] jsontools

    • [ g ] macos

    • [ g ] nmap

    • [ g ] pip

    • [ g ] pipenv

    • [ g ] python

    • [ g ] sbt

    • [ g ] scala

    • [ g ] ssh-agent

    • [ g ] sudo

    • [ g ] vagrant

    • [ g ] vscode

    • [ g ] web-search

    • [ g ] z

    • [ g ] zsh-syntax-highlighting

more

  • [ d ] Git in Zsh

  • [ h ] Antigen

  • [ h ] Prezto

  • [ h ][ g ] Spaceship Prompt

other