15. Parameter Substitution — Zsh 5.9.999.3-test documentation

# 15. Parameter Substitution

In `zsh`, parameters are set like this:

Example 15.1: Assigning a scalar

    % foo=bar
    % echo $foo
    bar

Spaces before or after the `=` are frowned upon:

Example 15.2: Spaces around equals fail

    % foo = bar
    zsh: command not found: foo

Also, `set` doesn’t work for setting parameters:

Example 15.3: set leaves parameter empty

    % set foo=bar
    % set foo = bar
    % echo $foo

    %

Note that no error message was printed. This is because both of these commands were perfectly valid; the `set` builtin assigns its arguments to the positional parameters (`$1`, `$2`, etc.).

Example 15.4: set fills positional params

    % set foo=bar
    % echo $1
    foo=bar
    % set foo = bar
    % echo $3 $2
    bar =

If you’re really intent on using the csh syntax, define a function like this:

Example 15.5: csh-style set function

    % set () {
    >    eval "$1$2$3"
    > }
    % set foo = bar
    % set fuu=brrr
    % echo $foo $fuu
    bar brrr

But then, of course you can’t use the form of `set` with options, like `set -F` (which turns off filename generation). Also, the `set` command by itself won’t list all the parameters like it should. To get around that you need a `case` statement:

Example 15.6: set function with options

    % set () {
    >    case $1 in
    >    -*|+*|'') builtin set $* ;;
    >    *) eval "$1$2$3" ;;
    >    esac
    > }

For the most part, this should make csh users happy.

The following sh-style operators are supported in `zsh`:

Example 15.7: Default and alternate values

    % unset null
    % echo ${foo-xxx}
    bar
    % echo ${null-xxx}
    xxx
    % unset null
    % echo ${null=xxx}
    xxx
    % echo $null
    xxx
    % echo ${foo=xxx}
    bar
    % echo $foo
    bar
    % unset null
    % echo ${null+set}

    % echo ${foo+set}
    set

Also, csh-style `:` modifiers may be appended to a parameter substitution.

Example 15.8: csh colon modifiers

    % echo $PWD
    /home/learning/pf/zsh/zsh2.00/src
    % echo $PWD:h
    /home/learning/pf/zsh/zsh2.00
    % echo $PWD:h:h
    /home/learning/pf/zsh
    % echo $PWD:t
    src
    % name=foo.c
    % echo $name
    foo.c
    % echo $name:r
    foo
    % echo $name:e
    c

The equivalent constructs in ksh (which are also supported in `zsh`) are a bit more general and easier to remember. When the shell expands `${foo#`pat`}`, it checks to see if pat matches a substring at the beginning of the value of `foo`. If so, it removes that portion of `foo`, using the shortest possible match. With `${foo##`pat`}`, the longest possible match is removed. `${foo%`pat`}` and `${foo%%`pat`}` remove the match from the end. Here are the ksh equivalents of the `:` modifiers:

Example 15.9: ksh pattern removal

    % echo ${PWD%/*}
    /home/learning/pf/zsh/zsh2.00
    % echo ${PWD%/*/*}
    /home/learning/pf/zsh
    % echo ${PWD##*/}
    src
    % echo ${name%.*}
    foo
    % echo ${name#*.}
    c

`zsh` also has upper/lowercase modifiers:

Example 15.10: Upper and lowercase modifiers

    % xx=Test
    % echo $xx:u
    TEST
    % echo $xx:l
    test

and a substitution modifier:

Example 15.11: String substitution modifier

    % echo $name:s/foo/bar/
    bar.c
    % ls
    foo.c    foo.h    foo.o    foo.pro
    % for i in foo.*; mv $i $i:s/foo/bar/
    % ls
    bar.c    bar.h    bar.o    bar.pro

There is yet another syntax to modify substituted parameters. You can add certain modifiers in parentheses after the opening brace like:

Example 15.12: Parenthesized modifiers syntax

    ${(modifiers)parameter}

For example, `o` sorts the words resulting from the expansion:

Example 15.13: Sorting with o modifier

    % echo ${path}
    /usr/bin /usr/bin/X11 /etc
    % echo ${(o)path}
    /etc /usr/bin /usr/bin/X11

One possible source of confusion is the fact that in `zsh`, the result of parameter substitution is not split into words. Thus, this will not work:

Example 15.14: No default word splitting

    % srcs='glob.c exec.c init.c'
    % ls $srcs
    glob.c exec.c init.c not found

This is considered a feature, not a bug. If splitting were done by default, as it is in most other shells, functions like this would not work properly:

Example 15.15: Preserving quoted arguments

    $ ll () { ls -F $* }
    $ ll 'fuu bar'
    fuu not found
    bar not found

    % ll 'fuu bar'
    fuu bar not found

Of course, a hackish workaround is available in sh (and `zsh`):

Example 15.16: SH_WORD_SPLIT with $@

    % setopt shwordsplit
    % ll () { ls -F "$@" }
    % ll 'fuu bar'
    fuu bar not found

If you like the sh behaviour, `zsh` can accommodate you:

Example 15.17: Forced word splitting

    % ls ${=srcs}
    exec.c  glob.c  init.c
    % setopt shwordsplit
    % ls $srcs
    exec.c  glob.c  init.c

Another way to get the `$srcs` trick to work is to use an array:

Example 15.18: Array assignment for files

    % unset srcs
    % srcs=( glob.c exec.c init.c )
    % ls $srcs
    exec.c  glob.c  init.c

or an alias:

Example 15.19: Global alias for files

    % alias -g SRCS='exec.c glob.c init.c'
    % ls SRCS
    exec.c  glob.c  init.c

Another option that modifies parameter expansion is `RC_EXPAND_PARAM`:

Example 15.20: RC_EXPAND_PARAM prefixes

    % echo foo/$srcs
    foo/glob.c exec.c init.c
    % setopt rcexpandparam
    % echo foo/$srcs
    foo/glob.c foo/exec.c foo/init.c
    % echo foo/${^srcs}
    foo/glob.c foo/exec.c foo/init.c
    % echo foo/$^srcs
    foo/glob.c foo/exec.c foo/init.c