Skip to content

Instantly share code, notes, and snippets.

@ClementNerma
Last active November 19, 2024 01:44
Show Gist options
  • Save ClementNerma/1dd94cb0f1884b9c20d1ba0037bdcde2 to your computer and use it in GitHub Desktop.
Save ClementNerma/1dd94cb0f1884b9c20d1ba0037bdcde2 to your computer and use it in GitHub Desktop.

ZSH CheatSheet

This is a cheat sheet for how to perform various actions to ZSH, which can be tricky to find on the web as the syntax is not intuitive and it is generally not very well-documented.

Strings

Description Syntax
Get the length of a string ${#VARNAME}
Get a single character ${VARNAME[index]}
Get the string from a specific index ${VARNAME[index,-1]}
Get a substring ${VARNAME[from,to]}
Replace the first occurrence in a string ${VARNAME/toreplace/replacement}
Replace all occurrences in a string ${VARNAME//toreplace/replacement}
Cut a string after a model ${VARNAME%%model*}
Check if a string starts by a specific substring if [[ $VARNAME = "startstr"* ]]
Check if a string contains a substring if [[ $VARNAME = *"substring"* ]]
Check if a string ends by a specific substring if [[ $VARNAME = *"substring" ]]

Arrays

Description Syntax
Create an array VARNAME=()
Create an array with initial values VARNAME=(value1 value2 value3)
Push to an array VARNAME+=(value)
Access an array's element VARNAME[index]
Remove first element from an array (shift) shift VARNAME
Remove last element from an array (pop) shift -p VARNAME
Get an array's length ${#VARNAME}
Iterate over an array's values for value in $VARNAME;
Get index of a value in an array (0 if not found) ${VARNAME[(Ie)value]}
Get index of a value in an array (${#VARNAME} + 1 if not found) ${VARNAME[(ie)value]}
Get an array slice after the specified index ${VARNAME:index}
Get an array slice after the specified index ${VARNAME:index:length}
Check if a value is contained in an array if (( $VARNAME[(Ie)value] ));
Check if an array is empty if [[ -z $VARNAME ]]
Check if an array is not empty if [[ ! -z $VARNAME ]]
Remove an element from an array VARNAME[index]=()

Associative arrays (= maps / dictionaries)

Associate arrays are the equivalent of hash maps or dictionaries in many other programming languages: unlike arrays, they can use string keys, and these don't necessary have an order.

Description Syntax
Create an associative array typeset -A VARNAME=()
Create an associative array with initial values typeset -A VARNAME=( [key1]=value1 [key2]=value2 )
Add a new key to the array VARNAME[key]=value
Access the array's elements $VARNAME[key]
Remove a key from the array unset 'VARNAME[key]'
Get the array's number of elements ${#VARNAME}
Iterate over the array's values for value in $VARNAME;
Iterate over the array's keys for key in ${(k)VARNAME};
Iterate over the array's key-value pairs for key value in ${(kv)VARNAME};

Arithmetics

Description Syntax
Compute a mathematical expression (variables don't need to be prefixed with $ in it) $((expression))

Variables

Description Syntax
Get the value of a variable whose name is in another variable ${(P)NAMEVAR}
Get the list of all defined variables, as an array ${(k)parameters}
Delete a variable unset VARNAME

Functions

Description Syntax
Declare a local variable (not accessible outside the function) local varname=...
Get the original executable name $0
Get a parameter $1 (second is $2, etc.)
Expand all parameters $*
Expand all parameters but keep them quoted if needed $@ (tip: it's an array!)
Get the number of parameters (so not counting $0) $#
Remove the first parameter from $@ shift
Remove the last parameter from $@ shift -p
Exit the function with a status code (behaves like for a command) return 1 (or any other code)
Get the list of all functions, as an array ${(k)functions}
Delete a function unset -f func_name

Aliases

Description Syntax
Display the list of all defined aliases alias
Get the list of all defined aliases, as an array ${(k)aliases}
Define an alias alias name="command arg1 arg2 arg3 ..."
Remove an alias unalias name
Get the arguments, with escaped spaces ${@:q}

Conditionals

A word on conditionals

Syntaxes:

# 1)
if expression
then
    # instructions
fi

# 2)
if expression; then
    # instructions
fi

# 3)
if expression; then ...; fi

# 4)
if expression; then
    # instructions
else
    # instructions
fi

# 5)
if expression; then
    # instructions
elif expression
    # instructions
else
    # instructions
fi
Description Syntax
Check if a string is empty or not defined if [[ -z $VARNAME ]];
Check if a string is defined and not empty if [[ -n $VARNAME ]];
Check if a file exists if [[ -f "filepath" ]];
Check if a directory exists if [[ -d "dirpath" ]];
Check if a symbolic link exists if [[ -L "symlinkpath" ]];
Check if a shell option is set if [[ -o OPTION_NAME ]];
Check if two values are equal if [[ $VAR1 = $VAR2 ]];
Check if two values are different if [[ $VAR1 != $VAR2 ]];
Check if a number is greater than another if (( $VAR1 > $VAR2 ));
Check if a number is smaller than another if (( $VAR1 < $VAR2 ));
Check if a command exits successfully (exit code 0) if command arg1 arg2 ...
Check if a command doesn't exit successfully (exit code != 0) if ! command arg1 arg2 ...

Note that the $ symbol preceding variables' names in arithmetic expression (((...))) are purely optional, so you can perfectly write if (( VAR1 < VAR2 )); for instance.

You can read all dash - options in ZSH's manual, as there are many different ones: http://zsh.sourceforge.net/Doc/Release/Conditional-Expressions.html

Loops

Syntaxes:

# 1)
for itervarname in iterable
do
    # instructions
done

# 2)
for itervarname in iterable; do
    # instructions
done

# 3)
for itervaname in iterable; do ...; done
Description Syntax
Iterate over a range (inclusive) for i in {from..to};
Iterate over a list of filesystem items for i in globpattern;
Iterate over a list of filesystem items, fail silently if no match found for i in globpattern(N);

Examples cheat sheet

Return a value from within a function:

function add() {
    local sum=$(($1 + $2))
    echo $sum
}

function add_twice() {
    local sum=$(add $1 $2) # get the callee's STDOUT
    local sum_twice=$(add $sum $sum)
    echo $sum_twice
}

echo $(add 2 3) # 5
echo $(add_twice 2 3) # 10

A word on conditionals

Conditionals use expressions, such as in if [[ -z $VARNAME ]]; the expression is [[ -z $VARNAME ]]. These can also be used in while loops, as well as be used outside of blocks:

[[ -z $VARNAME ]] && echo "VARNAME is not defined or empty!"
[[ -f $FILEPATH ]] && echo "File exists!"

This works because conditional expressions ([[ ... ]] and (( ... ))) don't actually return a value; they behave like commands and as such set the status code to 0 if the condition is true, or 1 else.

If we want to display the message only if the condition is falsey:

[[ -z $VARNAME ]] || echo "VARNAME is not empty!"
[[ -f $FILEPATH ]] || echo "File does not exist!"
@markjreed
Copy link

The note Get index of a value in an array (0 if not found) is incorrect; ${VARNAME[(ie)value]} does not return 0 if the item is not found. It instead returns one more than the length of the array.

@ClementNerma
Copy link
Author

The note Get index of a value in an array (0 if not found) is incorrect; ${VARNAME[(ie)value]} does not return 0 if the item is not found. It instead returns one more than the length of the array.

Indeed, it's with (Ie) that the index is 0. I fixed that in the document, thanks :) !

@xse
Copy link

xse commented Nov 22, 2021

bar | wc -l zsh-ism: (breaks tree-sitter bash syntax highlighting)

foo="${#${(@f)$(bar)}[@]}"

@jasonm23
Copy link

jasonm23 commented Jul 19, 2022

My google-fu is weak on something here. I'd like to find the docs where there's a list of all these zsh parameter flags?

For example:

(f)

(f) splits input on newline to populate an array (ignores blank lines)

an_array=("${(f)$(command)}")

(F)

(F) Join the contents of an array using newlines.

array(one two three four)

echo ${(F)array}

=>
one
two
three
four

(@)

Get all the contents of and array or associative array.

I think this does more but I'm not sure...

array=(1 2 3 4 5 6)

echo ${(@)array}

=> 1 2 3 4 5 6

It can be used with (f) to keep blank lines from being skipped.

an_array=("${(@f)$(command)}")

(k) and (v)

Accessing associative arrays (k) keys, or (v) values.

aso=(key value another\ key another\ value another\ day dollars)

echo ${(k)aso}

(q)

Quote all the heck all the way out of input:

filename="/Volumes/My Drive/Something With [stuff!] that needs escaping (ie. quoting).txt"
echo ${(q)filename}

=> /Volumes/My\ Drive/Something\ With\ \[stuff!\]\ that\ needs\ escaping\ \(ie.\ quoting\).txt

(qq)

Skip the escaping and use single quotes instead...

filename="/Volumes/My Drive/Something With [stuff!] that needs escaping (ie. quoting).txt"
echo ${(qq)filename}

=> '/Volumes/My Drive/Something With [stuff!] that needs escaping (ie. quoting).txt'

(qqq)

Escape/quote chars that would be special in a regualar double quoted string...

myvar='This is ${not_to_be_expanded}...'
echo ${(qqq)myvar}

=> "This is \${not_to_be_expanded}..."

(Q)

Unquote input:

filename=/Volumes/My\ Drive/Something\ With\ \[stuff!\]\ that\ needs\ escaping\ \(ie.\ quoting\).txt
echo ${(Q)filename}

=> "/Volumes/My Drive/Something With [stuff!] that needs escaping (ie. quoting).txt"

(r) and (R)

Array search (r) from the start, or (R) end.

array=(this is a test of \(r\))

echo ${array[(r)t*]}
=> this

echo ${array[(R)t*]}
=> test

(i) and (I)

Array search for index (i) from the start, or (I) end.

array=(this is a test of \(i\))

echo ${array[(i)t*]}
=> 1

echo ${array[(I)t*]}
=> 2

(c) & (w)

Should count the chars or words in an array. (Although this did not work for me...)

Are there more...?!?

Yes, so many, and so few examples!

@ClementNerma
Copy link
Author

ClementNerma commented Jul 19, 2022

There is such a list at: https://zsh.sourceforge.io/Guide/zshguide05.html

It's just very long (about 50 pages when printed) and not intuitive at all.

@jasonm23
Copy link

Ah, so (c) and (w) need to be in the form (c)#name / (w)#name.

@jasonm23
Copy link

@ClementNerma - that's the deal, many flags and whatnot but no examples. At least this Gist provides a place to throw more examples.

@jasonm23
Copy link

@ClementNerma https://zsh.sourceforge.io/Guide/zshguide05.html - To be fair, I've seen worse than this.

@jasonm23
Copy link

(u) (plus (M)

(u)nique elements which (M)atch "findme"

myarr=("${(M@)${(uf)$(< myfile)}:#*findme*}")

@jasonm23
Copy link

jasonm23 commented Jul 19, 2022

I suppose it's also worth adding a general note...

Rule 1. Never write a big complex script in any shell, you will regret not using a real programming language sooner or later.

@akharrou
Copy link

akharrou commented Jul 27, 2022

thanks for your comments @jasonm23, btw regarding quoting, there's also (qqqq) which: quotes result with $'...'

  $ filename="/Volumes/My Drive/Something With [stuff\!] that needs escaping (ie. quoting).txt"
  $ echo ${(q)filename}
    echo ${(qq)filename}
    echo ${(qqq)filename}
    echo ${(qqqq)filename}
    echo ${(Q)filename}
  /Volumes/My\ Drive/Something\ With\ \[stuff\!\]\ that\ needs\ escaping\ \(ie.\ quoting\).txt
  '/Volumes/My Drive/Something With [stuff!] that needs escaping (ie. quoting).txt'
  "/Volumes/My Drive/Something With [stuff\!] that needs escaping (ie. quoting).txt"
  $'/Volumes/My Drive/Something With [stuff\!] that needs escaping (ie. quoting).txt'
  /Volumes/My Drive/Something With [stuff!] that needs escaping (ie. quoting).txt

here's the really cool cheatsheets I got that from:

and some more resources:

@jasonm23
Copy link

Nice cheatsheet.

@jasonm23
Copy link

jasonm23 commented Jul 28, 2022

TIL I don't know about $'...'

I guess I do now... https://unix.stackexchange.com/a/607085/1335

@jasonm23
Copy link

jasonm23 commented Aug 5, 2022

Can anyone say what the difference is between:

${var%match*} and ${var%%match*}

I've only used ${var%match*} and they seem to be identical.

@markjreed
Copy link

markjreed commented Aug 5, 2022 via email

@ferdnyc
Copy link

ferdnyc commented May 11, 2024

Huh, I'm surprised the expansion modifiers aren't covered above, or in @akharrou 's otherwise rather snazzy cheatsheet find. Maybe they get overlooked because they're documented in the History Expansion section of zshexpn(1), but there is a note that (most) also work for filename generation and parameter expansion.

The one I use all the time is :r, to remove the last suffix from a filename. (${var:r} is basically a handy shorthand for ${var%.*}.) Great for writing loops to convert files between formats:

for img in $some_list_of_bmp_trash; do
    convert $img ${img:r}.png
done

But there's plenty more, like:

Modifier Purpose Examples/equivalents
${var:e} Remove all but the last extension "${var:r}.${var:e}" almost == "${var}", unless the last part of ${var} contains no .. (Then it would mistakenly add one.)
${var:l}
${var:u}
Lowercase or uppercase the variable's value Same as ${(L)var} and ${(U)var} but easier to type.
${var:P} Make a path canonical and absolute ${fname:P} == $(realpath $fname)
${var:hN} Go up N levels in the path hierarchy Like wrapping N nested $(dirname $(dirname etc...) ) calls around the var.
${var:tN} Leave N levels of hierarchy at the end of a path Tail-end complement to ${var:hN}. With N == 0 or 1 (or omitted), it's just $(basename $var). With N > 1, an operation that's difficult to express with similar tools. Call it "tailname".
var="a/path/of/any/depth/to/a/file"; echo "${var:t3}" => to/a/file

@andelink
Copy link

I'd like to find the docs where there's a list of all these zsh parameter flags?

@jasonm23 the official reference is here: https://zsh.sourceforge.io/Doc/Release/Expansion.html

@ferdnyc
Copy link

ferdnyc commented Sep 12, 2024

I actually got my explanation of ${var:hN} wrong, above. The same way ${var:tN} walks back from the filename part of the path, the :hN form walks down from the start of the path. So it's nothing like dirname at all.

var="a/path/of/any/depth/to/a/file"; echo "${var:h3}" => a/path/of

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment