aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/shells
diff options
context:
space:
mode:
Diffstat (limited to 'src/shells')
-rw-r--r--src/shells/README.md5
-rw-r--r--src/shells/bash.md259
-rw-r--r--src/shells/fish.md280
-rw-r--r--src/shells/zsh.md371
4 files changed, 915 insertions, 0 deletions
diff --git a/src/shells/README.md b/src/shells/README.md
new file mode 100644
index 0000000..3a7c121
--- /dev/null
+++ b/src/shells/README.md
@@ -0,0 +1,5 @@
+# Shells
+
+- [zsh](./zsh.md)
+- [bash](./bash.md)
+- [fish](./fish.md)
diff --git a/src/shells/bash.md b/src/shells/bash.md
new file mode 100644
index 0000000..f27b3a2
--- /dev/null
+++ b/src/shells/bash.md
@@ -0,0 +1,259 @@
+# bash(1)
+
+## Expansion
+
+### Generator
+
+```bash
+# generate sequence from n to m
+{n..m}
+# generate sequence from n to m step by s
+{n..m..s}
+
+# expand cartesian product
+{a,b}{c,d}
+```
+
+### Parameter
+```bash
+# default value
+bar=${foo:-some_val} # if $foo set, then bar=$foo else bar=some_val
+
+# alternate value
+bar=${foo:+bla $foo} # if $foo set, then bar="bla $foo" else bar=""
+
+# check param set
+bar=${foo:?msg} # if $foo set, then bar=$foo else exit and print msg
+
+# indirect
+FOO=foo
+BAR=FOO
+bar=${!BAR} # deref value of BAR -> bar=$FOO
+
+# prefix
+${foo#prefix} # remove prefix when expanding $foo
+# suffix
+${foo%suffix} # remove suffix when expanding $foo
+
+# substitute
+${foo/pattern/string} # replace pattern with string when expanding foo
+# pattern starts with
+# '/' replace all occurences of pattern
+# '#' pattern match at beginning
+# '%' pattern match at end
+
+# set programmatically with priintf builtin
+printf -v "VAR1" "abc"
+NAME=VAR2
+printf -v "$NAME" "%s" "def"
+```
+
+> Note: `prefix`/`suffix`/`pattern` are expanded as [pathnames](#pathname).
+
+### Pathname
+
+```bash
+* match any string
+? match any single char
+\\ match backslash
+[abc] match any char of 'a' 'b' 'c'
+[a-z] match any char between 'a' - 'z'
+[^ab] negate, match all not 'a' 'b'
+[:class:] match any char in class, available:
+ alnum,alpha,ascii,blank,cntrl,digit,graph,lower,
+ print,punct,space,upper,word,xdigit
+```
+
+With `extglob` shell option enabled it is possible to have more powerful
+patterns. In the following `pattern-list` is one ore more patterns separated
+by `|` char.
+
+```bash
+?(pattern-list) matches zero or one occurrence of the given patterns
+*(pattern-list) matches zero or more occurrences of the given patterns
++(pattern-list) matches one or more occurrences of the given patterns
+@(pattern-list) matches one of the given patterns
+!(pattern-list) matches anything except one of the given patterns
+```
+> Note: `shopt -s extglob`/`shopt -u extglob` to enable/disable `extglob`
+> option.
+
+## I/O redirection
+
+> Note: The trick with bash I/O redirection is to interpret from left-to-right.
+
+```bash
+# stdout & stderr to file
+command >file 2>&1
+# equivalent
+command &>file
+
+# stderr to stdout & stdout to file
+command 2>&1 >file
+```
+> The article [Bash One-Liners Explained, Part III: All about
+> redirections](https://catonmat.net/bash-one-liners-explained-part-three)
+> contains some nice visualization to explain bash redirections.
+
+### Explanation
+
+```bash
+j>&i
+```
+Duplicate `fd i` to `fd j`, making `j` a copy of `i`. See [dup2(2)][dup2].
+
+Example:
+```bash
+command 2>&1 >file
+```
+1. duplicate `fd 1` to `fd 2`, effectively redirecting `stderr` to `stdout`
+2. redirect `stdout` to `file`
+
+## Process substitution ([ref][psub])
+
+Process substitution allows to redirect the stdout of multiple processes at
+once.
+```bash
+vim -d <(grep foo bar) <(grep foo moose)
+```
+
+## Command grouping
+
+Execute commands in a group with or without subshell. Can be used to easily
+redirect stdout/stderr of all commands in the group into one file.
+```bash
+# Group commands without subshell.
+v=abc ; { v=foo; echo $v; } ; echo $v
+# foo
+# foo
+
+# Group commands with subshell.
+v=abc ; ( v=foo; echo $v; ) ; echo $v
+# foo
+# abc
+```
+
+## Argument parsing with `getopts`
+
+The `getopts` builtin uses following global variables:
+- `OPTARG`, value of last option argument
+- `OPTIND`, index of the next argument to process (user must reset)
+- `OPTERR`, display errors if set to `1`
+
+```bash
+getopts <optstring> <param> [<args>]
+```
+- `<optstring>` specifies the names of supported options, eg `f:c`
+ - `f:` means `-f` option with an argument
+ - `c` means `-c` option without an argument
+- `<param>` specifies a variable name which `getopts` fills with the last parsed option argument
+- `<args>` optionally specify argument string to parse, by default `getopts` parses `$@`
+
+### Example
+```bash
+#!/bin/bash
+function parse_args() {
+ while getopts "f:c" PARAM; do
+ case $PARAM in
+ f) echo "GOT -f $OPTARG";;
+ c) echo "GOT -c";;
+ *) echo "ERR: print usage"; exit 1;;
+ esac
+ done
+ # users responsibility to reset OPTIND
+ OPTIND=1
+}
+
+parse_args -f xxx -c
+parse_args -f yyy
+```
+
+## Regular Expressions
+
+Bash supports regular expression matching with the binary operator `=~`.
+The match results can be accessed via the `$BASH_REMATCH` variable:
+- `${BASH_REMATCH[0]}` contains the full match
+- `${BASH_REMATCH[1]}` contains match of the first capture group
+
+```bash
+INPUT='title foo : 1234'
+REGEX='^title (.+) : ([0-9]+)$'
+if [[ $INPUT =~ $REGEX ]]; then
+ echo "${BASH_REMATCH[0]}" # title foo : 1234
+ echo "${BASH_REMATCH[1]}" # foo
+ echo "${BASH_REMATCH[2]}" # 1234
+fi
+```
+> **Caution**: When specifying a `regex` in the `[[ ]]` block directly, quotes will be treated as part of the pattern.
+> `[[ $INPUT =~ "foo" ]]` will match against `"foo"` not `foo`!
+
+## Completion
+
+The `complete` builtin is used to interact with the completion system.
+```bash
+complete # print currently installed completion handler
+complete -F <func> <cmd> # install <func> as completion handler for <cmd>
+complete -r <cmd> # uninstall completion handler for <cmd>
+```
+
+Variables available in completion functions:
+```bash
+# in
+$1 # <cmd>
+$2 # current word
+$3 # privous word
+
+COMP_WORDS # array with current command line words
+COMP_CWORD # index into COMP_WORDS with current cursor position
+
+# out
+COMPREPLY # array with possible completions
+```
+
+The `compgen` builtin is used to generate possible matches by comparing `word`
+against words generated by `option`.
+```bash
+compgen <option> <word>
+
+# usefule options:
+# -W <list> specify list of possible completions
+# -d generate list with dirs
+# -f generate list with files
+# -u generate list with users
+# -e generate list with exported variables
+
+# compare "f" against words "foo" "foobar" "bar" and generate matches
+compgen -W "foo foobar bar" "f"
+
+# compare "hom" against file/dir names and generate matches
+compgen -d -f "hom"
+```
+
+### Example
+Skeleton to copy/paste for writing simple completions.
+
+Assume a program `foo` with the following interface:
+```bash
+foo -c green|red|blue -s low|high -f <file> -h
+```
+
+The completion handler could be implemented as follows:
+```bash
+function _foo() {
+ local curr=$2
+ local prev=$3
+
+ local opts="-c -s -f -h"
+ case $prev in
+ -c) COMPREPLY=( $(compgen -W "green red blue" -- $curr) );;
+ -s) COMPREPLY=( $(compgen -W "low high" -- $curr) );;
+ -f) COMPREPLY=( $(compgen -f -- $curr) );;
+ *) COMPREPLY=( $(compgen -W "$opts" -- $curr) );;
+ esac
+}
+
+complete -F _foo foo
+```
+
+[dup2]: http://man7.org/linux/man-pages/man2/dup.2.html
+[psub]: https://tldp.org/LDP/abs/html/process-sub.html
diff --git a/src/shells/fish.md b/src/shells/fish.md
new file mode 100644
index 0000000..a499e10
--- /dev/null
+++ b/src/shells/fish.md
@@ -0,0 +1,280 @@
+# fish(1)
+
+## Quick Info
+Fish initialization file `~/.config/fish/config.fish`
+
+Switch between different key bindings:
+- `fish_default_key_bindings` to use default key bindings
+- `fish_vi_key_bindings` to use vi key bindings
+
+## Variables
+Available scopes
+- `local` variable local to a block
+- `global` variable global to shell instance
+- `universal` variable universal to all shell instances + preserved across
+ shell restart
+
+### Set/Unset Variables
+```text
+set <name> [<values>]
+ -l local scope
+ -g global scope
+ -U universal scope
+ -e erase variable
+ -S show verbose info
+ -x export to ENV
+ -u unexport from ENV
+```
+
+### Special Variables [ref](https://fishshell.com/docs/current/language.html#special-variables)
+```sh
+$status # exit code of last command
+$pipestatus # list of exit codes of pipe chain
+
+$fish_pid # pid of parent fish shell ($$ in bash)
+$last_pid # pid of last started process ($! in bash)
+
+$CMD_DURATION # runtime of last command in ms
+```
+
+### Lists
+In `fish` all variables are lists (start with index `1`, but lists can't
+contain lists.
+```sh
+set foo a b c d
+
+echo $foo[1] # a
+echo $foo[-1] # d
+echo $foo[2..3] # b c
+echo $foo[1 3] # a c
+```
+
+`$` can be seen as dereference operator.
+```sh
+set foo a; set a 1337
+echo $$foo # outputs 1337
+```
+
+Cartesian product.
+```sh
+echo file.{h,cc}
+# file.h file.cc
+
+echo {a,b}{1,2}
+# a1 b1 a2 b2
+```
+
+#### `*PATH` [ref](https://fishshell.com/docs/current/language.html#path-variables)
+Lists ending with `PATH` are automatically split at `:` when used and joined
+with `:` when quoted or exported to the environment.
+```sh
+set -x BLA_PATH a:b:c:d
+echo $BLA_PATH # a b c d
+echo "$BLA_PATH" # a:b:c:d (quoted)
+env | grep BLA_PATH # BLA_PATH=a:b:c:d (env)
+
+set FOO_PATH x y z
+echo $FOO_PATH # x y z
+echo "$FOO_PATH" # x:y:z
+```
+
+## Command Handling
+```sh
+# sub-commands are not run in quotes
+echo "ls output: "(ls)
+```
+
+### I/O redirection
+```sh
+# 'noclobber', fail if 'log' already exists
+echo foo >? log
+```
+
+### Process substitution
+Redirect output of multiple processes. Same as `<(..)` in bash.
+```sh
+diff (sort a | psub) (sort b | psub)
+```
+
+## Control Flow
+### `if` / `else`
+```sh
+if grep foo bar
+ # do sth
+else if grep foobar bar
+ # do sth else
+else
+ # do sth else
+end
+```
+
+### `switch`
+```sh
+switch (echo foo)
+case 'foo*'
+ # do start with foo
+case bar dudel
+ # do bar and dudel
+case '*'
+ # do else
+end
+```
+
+### `while` Loop
+```sh
+while true
+ echo foo
+end
+```
+
+### `for` Loop
+```sh
+for f in (ls)
+ echo $f
+end
+```
+
+## Functions
+Function arguments are passed via `$argv` list.
+```sh
+function fn_foo
+ echo $argv
+end
+```
+
+### Autoloading
+When running a command fish attempts to autoload a function. The shell looks
+for `<cmd>.fish` in the locations defined by `$fish_function_path` and loads
+the function lazily if found.
+
+This is the preferred way over monolithically defining all functions in a
+startup script.
+
+### Helper
+```sh
+functions # list al functions
+functions foo # describe function 'foo'
+functions -e foo # erase function 'foo'
+
+funced foo # edit function 'foo'
+ # '-e vim' to edit in vim
+```
+
+### Argument parsing and completion
+`argparse` puts options into variables of name `_flag_NAME`.
+
+References:
+- [Argument Handling](https://fishshell.com/docs/current/language.html#argument-handling)
+- [`argparse`](https://fishshell.com/docs/current/cmds/argparse.html)
+- [Writing your own completions](https://fishshell.com/docs/current/completions.html)
+- [`complete`](https://fishshell.com/docs/current/cmds/complete.html)
+
+```sh
+function moose --d "my moose fn"
+ # h/help : short / long option (boolean)
+ # color : only long option (boolean)
+ # u/user= : option with required argument, only last specified is taken
+ # f/file+= : option with required argument, can be specified multiple times
+ #
+ argparse h/help color= u/user= f/file=+ -- $argv
+ or return
+
+ if set -ql _flag_help
+ echo "usage ..."
+ return 0
+ end
+
+ set -ql _flag_file
+ and echo "file=$_flag_file | cnt:" (count $_flag_file)
+
+ set -ql _flag_color
+ and echo "color=$_flag_color"
+
+ set -ql _flag_user
+ and echo "user=$_flag_user"
+end
+
+# Delete all previous defined completions for 'moose'.
+complete -c moose -e
+
+# Don't complete files for command.
+complete -c moose --no-files
+
+# Help completion.
+# -n specifies a conditions. The completion is only active if the command
+# returns 0.
+complete -c moose -s h -l help -n "not __fish_contains_opt -s h help" \
+ --description "Print usage help and exit"
+
+# File completion.
+# -F force complete files (overwrite --no-files).
+# -r requires argument.
+complete -c moose -s f -l file -F -r \
+ --description "Specify file (can be passed multiple times)"
+
+# Color completion.
+# -a options for completion.
+# -x short for -r and --no-files (-f)
+complete -c moose -x -l color -a "red blue" \
+ --description "Specify a color."
+
+# User completion.
+# -a options for completion. Call a function to generate arguments.
+complete -c moose -x -s u -l user -a "(__fish_complete_users)" \
+ --description "Specify a user"
+```
+
+## Prompt
+The prompt is defined by the output of the `fish_prompt` function.
+```sh
+function fish_prompt
+ set -l cmd_ret
+ echo "> "(pwd) $cmd_ret" "
+end
+```
+> Use `set_color` to manipulate terminal colors and `set_color -c` to print the
+> current colors.
+
+
+## Useful Builtins
+List all builtins with `builtins -n`.
+
+```sh
+# history
+history search <str> # search history for <str>
+history merge # merge histories from fish sessions
+
+# list
+count $var # count elements in list
+
+contains /bin $PATH # return 0 (true) 1 (false)
+contains -i /bin $PATH # additionally print index on stdout
+
+# string
+string split SEP STRING
+
+# math
+math -b hex 4096 # output dec as hex
+math 0x1000 # output hex as dec
+math "log2(1024)" # call functions
+math -s0 7/3 # integer division (by default float)
+
+# status
+status -f # abs path of current file
+```
+
+## Keymaps
+```text
+ Shift-Tab .............. tab-completion with search
+ Alt-Up / Alt-Down ...... search history with token under the cursor
+ Alt-l .................. list content of dir under cursor
+ Alt-p .................. append '2>&1 | less;' to current cmdline
+ Alt-Left / Alt - Right . prevd / nextd, walk dir history
+```
+
+## Debug
+```text
+ status print-stack-trace .. prints function stacktrace (can be used in scripts)
+ breakpoint ................ halt script execution and gives shell (C-d | exit
+ to continue)
+```
diff --git a/src/shells/zsh.md b/src/shells/zsh.md
new file mode 100644
index 0000000..f702655
--- /dev/null
+++ b/src/shells/zsh.md
@@ -0,0 +1,371 @@
+# zsh(1)
+
+## Keybindings
+
+Change input mode:
+```zsh
+bindkey -v change to vi keymap
+bindkey -e change to emacs keymap
+```
+
+Define key-mappings:
+```zsh
+bindkey list mappings in current keymap
+bindkey in-str cmd create mapping for `in-str` to `cmd`
+bindkey -r in-str remove binding for `in-str`
+
+# C-v <key> dump <key> code, which can be used in `in-str`
+# zle -l list all functions for keybindings
+# man zshzle(1) STANDARD WIDGETS: get description of functions
+```
+
+Access edit buffer in zle widget:
+```zsh
+$BUFFER # Entire edit buffer content
+$LBUFFER # Edit buffer content left to cursor
+$RBUFFER # Edit buffer content right to cursor
+
+# create zle widget which adds text right of the cursor
+function add-text() {
+ RBUFFER="some text $RBUFFER"
+}
+zle -N add-text
+
+bindkey "^p" add-text
+```
+
+## Parameter
+
+Default value:
+```zsh
+# default value
+echo ${foo:-defval} # defval
+foo=bar
+echo ${foo:-defval} # bar
+```
+
+Alternative value:
+```zsh
+echo ${foo:+altval} # ''
+foo=bar
+echo ${foo:+altval} # altval
+```
+
+Check variable set, error if not set:
+```zsh
+echo ${foo:?msg} # print `msg` and return errno `1`
+foo=bar
+echo ${foo:?msg} # bar
+```
+
+Sub-string `${var:offset:length}`:
+```zsh
+foo=abcdef
+echo ${foo:1:3} # bcd
+```
+
+Trim prefix `${var#prefix}`:
+```zsh
+foo=bar.baz
+echo ${foo#bar} # .baz
+```
+
+Trim suffix `${var%suffix}`:
+```zsh
+foo=bar.baz
+echo ${foo%.baz} # bar
+```
+
+Substitute pattern `${var/pattern/replace}`:
+```zsh
+foo=aabbccbbdd
+echo ${foo/bb/XX} # aaXXccbbdd
+echo ${foo//bb/XX} # aaXXccXXdd
+# replace prefix
+echo ${foo/#bb/XX} # aabbccbbdd
+echo ${foo/#aa/XX} # XXbbccbbdd
+# replace suffix
+echo ${foo/%bb/XX} # aabbccbbdd
+echo ${foo/%dd/XX} # aabbccbbXX
+```
+
+> Note: `prefix`/`suffix`/`pattern` are expanded as pathnames.
+
+## Variables
+
+```zsh
+# Variable with local scope
+local var=val
+
+# Read-only variable
+readonly var=bal
+```
+
+Indexed arrays:
+```zsh
+arr=(aa bb cc dd)
+echo $arr[1] # aa
+echo $arr[-1] # dd
+
+arr+=(ee)
+echo $arr[-1] # ee
+
+echo $arr[1,3] # aa bb cc
+```
+
+Associative arrays:
+```zsh
+typeset -A arr
+arr[x]='aa'
+arr[y]='bb'
+echo $arr[x] # aa
+```
+
+Tied arrays:
+```zsh
+typeset -T VEC vec=(1 2 3) '|'
+
+echo $vec # 1 2 3
+echo $VEC # 1|2|3
+```
+
+Unique arrays (set):
+```
+typeset -U vec=(1 2 3)
+
+echo $vec # 1 2 3
+vec+=(1 2 4)
+echo $vec # 1 2 3 4
+```
+
+### Expansion Flags
+
+Join array to string `j:sep:`:
+```zsh
+foo=(1 2 3 4)
+echo ${(j:-:)foo} # 1-2-3-4
+echo ${(j:\n:)foo} # join with new lines
+```
+
+Split string to array `s:sep`:
+```zsh
+foo='1-2-3-4'
+bar=(${(s:-:)foo}) # capture as array
+echo $bar # 1 2 3 4
+echo $bar[2] # 2
+```
+
+Upper/Lower case string:
+```zsh
+foo=aaBB
+echo ${(L)foo} # aabb
+echo ${(U)foo} # AABB
+```
+
+Key/values in associative arrays:
+```zsh
+typeset -A vec; vec[a]='aa'; vec[b]='bb'
+
+echo ${(k)vec} # a b
+echo ${(v)vec} # aa bb
+echo ${(kv)vec} # a aa b bb
+
+# Iterate over key value pairs.
+for k v in ${(kv)vec)}; do ...; done
+```
+
+## I/O redirections
+
+See [bash - I/O redirection](bash.md#io-redirection)
+
+## Process substitution
+
+Process substitution allows to redirect the stdout of multiple processes at
+once.
+```bash
+vim -d <(grep foo bar) <(grep foo moose)
+```
+
+## Argument parsing with `zparseopts`
+
+```zsh
+zparseopts [-D] [-E] [-A assoc] specs
+```
+Arguments are copied into the associative array `assoc` according to `specs`.
+Each spec is described by an entry as `opt[:][=array]`.
+- `opt` is the option without the `-` char. Passing `-f` is matched against `f`
+ opt, `--long` is matched against `-long`.
+- Using `:` means the option will take an argument.
+- The optional `=array` specifies an alternate storage container where this
+ option should be stored.
+> Documentation can be found in `man zshmodules`.
+
+### Example
+```zsh
+#!/bin/zsh
+function test() {
+ zparseopts -D -E -A opts f=flag o: -long:
+ echo "flag $flag"
+ echo "o $opts[-o]"
+ echo "long $opts[--long]"
+ echo "pos $1"
+}
+
+test -f -o OPTION --long LONG_OPT POSITIONAL
+
+# Outputs:
+# flag -f
+# o OPTION
+# long LONG_OPT
+# pos POSITIONAL
+```
+
+## Regular Expressions
+
+Zsh supports regular expression matching with the binary operator `=~`.
+The match results can be accessed via the `$MATCH` variable and
+`$match` indexed array:
+- `$MATCH` contains the full match
+- `$match[1]` contains match of the first capture group
+
+```zsh
+INPUT='title foo : 1234'
+REGEX='^title (.+) : ([0-9]+)$'
+if [[ $INPUT =~ $REGEX ]]; then
+ echo "$MATCH" # title foo : 1234
+ echo "$match[1]" # foo
+ echo "$match[2]" # 1234
+fi
+```
+
+## Completion
+
+### Installation
+
+Completion functions are provided via files and need to be placed in a location
+covered by `$fpath`. By convention the completion files are names as `_<CMD>`.
+
+A completion skeleton for the command `foo`, stored in `_foo`
+```zsh
+#compdef _foo foo
+
+function _foo() {
+ ...
+}
+```
+
+Alternatively one can install a completion function explicitly by calling `compdef <FUNC> <CMD>`.
+
+### Completion Variables
+
+Following variables are available in Completion functions:
+```zsh
+$words # array with command line in words
+$#words # number words
+$CURRENT # index into $words for cursor position
+$words[CURRENT-1] # previous word (relative to cursor position)
+```
+
+### Completion Functions
+- `_describe` simple completion, just words + description
+- `_arguments` sophisticated completion, allow to specify actions
+
+#### Completion with [`_describe`][zsh-comp-fn]
+```zsh
+_describe MSG COMP
+```
+- `MSG` simple string with header message
+- `COMP` array of completions where each entry is `"opt:description"`
+
+```zsh
+function _foo() {
+ local -a opts
+ opts=('bla:desc for bla' 'blu:desc for blu')
+ _describe 'foo-msg' opts
+}
+compdef _foo foo
+
+foo <TAB><TAB>
+ -- foo-msg --
+bla -- desc for bla
+blu -- desc for blu
+```
+
+#### Completion with [`_arguments`][zsh-comp-fn]
+```zsh
+_arguments SPEC [SPEC...]
+```
+where `SPEC` can have one of the following forms:
+- `OPT[DESC]:MSG:ACTION` for option flags
+- `N:MSG:ACTION` for positional arguments
+
+Available actions
+```zsh
+(op1 op2) list possible matches
+->VAL set $state=VAL and continue, `$state` can be checked later in switch case
+FUNC call func to generate matches
+{STR} evaluate `STR` to generate matches
+```
+
+### Example
+Skeleton to copy/paste for writing simple completions.
+
+Assume a program `foo` with the following interface:
+```zsh
+foo -c green|red|blue -s low|high -f <file> -d <dir> -h
+```
+
+The completion handler could be implemented as follows in a file called `_foo`:
+```zsh
+#compdef _foo foo
+
+function _foo_color() {
+ local colors=()
+ colors+=('green:green color')
+ colors+=('red:red color')
+ colors+=('blue:blue color')
+ _describe "color" colors
+}
+
+function _foo() {
+ _arguments \
+ "-c[define color]:color:->s_color" \
+ "-s[select sound]:sound:(low high)" \
+ "-f[select file]:file:_files" \
+ "-d[select dir]:dir:_files -/" \
+ "-h[help]"
+
+ case $state in
+ s_color) _foo_color;;
+ esac
+}
+```
+
+### Example with optional arguments
+For this example we assume that the command `foo` takes at least three optional
+arguments such as
+```zsh
+foo arg1 arg2 arg3 [argN..]
+```
+
+```zsh
+function _foo() {
+ _arguments \
+ "1:opt 1:(a b c)" \
+ ":opt next:(d e f)" \
+ "*:opt all:(u v w)"
+}
+```
+Explanation:
+- `1:MSG:ACTION` sets completion for the **first** optional argument
+- `:MSG:ACTION` sets completion for the **next** optional argument
+- `*:MSG:ACTION` sets completion for the optional argument where none of the
+ previous rules apply, so in our example for `arg3, argN..`.
+
+> `_files` is a zsh builtin utility function to complete files/dirs see
+> - [zsh completion functions][zsh-comp-fn]
+> - [zsh completion utility functions][zsh-comp-utility-fn]
+
+
+[zsh-comp-fn]: http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions
+[zsh-comp-utility-fn]: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#utility-functions