.. _intro_zsh_parameter_substitution:

Parameter Substitution
======================

In :literal:`zsh`, parameters are set like this:

.. code-block:: text
   :caption: Assigning a scalar
   :name: params_assign_scalar

   % foo=bar
   % echo $foo
   bar

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

.. code-block:: text
   :caption: Spaces around equals fail
   :name: params_spaces_fail

   % foo = bar
   zsh: command not found: foo

Also, :literal:`set` doesn't work for setting parameters:

.. code-block:: text
   :caption: set leaves parameter empty
   :name: params_set_no_assign

   % 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 :literal:`set` builtin assigns its arguments to the :emphasis:`positional parameters` (\ :literal:`$1`, :literal:`$2`, etc.).

.. code-block:: text
   :caption: set fills positional params
   :name: params_set_positional

   % 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:

.. code-block:: text
   :caption: csh-style set function
   :name: params_set_csh_func

   % 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 :literal:`set` with options, like :literal:`set -F` (which turns off filename generation). Also, the :literal:`set` command by itself won't list all the parameters like it should. To get around that you need a :literal:`case` statement:

.. code-block:: text
   :caption: set function with options
   :name: params_set_case_func

   % 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 :literal:`zsh`:

.. code-block:: text
   :caption: Default and alternate values
   :name: params_default_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 :literal:`:` modifiers may be appended to a parameter substitution.

.. code-block:: text
   :caption: csh colon modifiers
   :name: params_csh_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 :literal:`zsh`) are a bit more general and easier to remember. When the shell expands :literal:`${foo#`\ :emphasis:`pat`\ :literal:`}`, it checks to see if :emphasis:`pat` matches a substring at the beginning of the value of :literal:`foo`.  If so, it removes that portion of :literal:`foo`, using the shortest possible match. With :literal:`${foo##`\ :emphasis:`pat`\ :literal:`}`, the longest possible match is removed. :literal:`${foo%`\ :emphasis:`pat`\ :literal:`}` and :literal:`${foo%%`\ :emphasis:`pat`\ :literal:`}` remove the match from the end. Here are the ksh equivalents of the :literal:`:` modifiers:

.. code-block:: text
   :caption: ksh pattern removal
   :name: params_ksh_patterns

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

:literal:`zsh` also has upper/lowercase modifiers:

.. code-block:: text
   :caption: Upper and lowercase modifiers
   :name: params_case_modifiers

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

and a substitution modifier:

.. code-block:: text
   :caption: String substitution modifier
   :name: params_subst_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:

.. code-block:: text
   :caption: Parenthesized modifiers syntax
   :name: params_paren_syntax

   ${(modifiers)parameter}

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

.. code-block:: text
   :caption: Sorting with o modifier
   :name: params_sort_o

   % 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 :literal:`zsh`, the result of parameter substitution is :emphasis:`not` split into words.  Thus, this will not work:

.. code-block:: text
   :caption: No default word splitting
   :name: params_no_split

   % 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:

.. code-block:: text
   :caption: Preserving quoted arguments
   :name: params_preserve_quotes

   $ 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 :literal:`zsh`):

.. code-block:: text
   :caption: SH_WORD_SPLIT with $@
   :name: params_sh_word_split

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

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

.. code-block:: text
   :caption: Forced word splitting
   :name: params_force_split

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

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

.. code-block:: text
   :caption: Array assignment for files
   :name: params_array_files

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

or an alias:

.. code-block:: text
   :caption: Global alias for files
   :name: params_global_alias

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

.. _RC_EXPAND_PARAM:

.. index:: single: RC_EXPAND_PARAM

Another option that modifies parameter expansion is :literal:`RC_EXPAND_PARAM`:

.. code-block:: text
   :caption: RC_EXPAND_PARAM prefixes
   :name: params_rc_expand

   % 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
