[ prev | next | top ]

## 15. Parameter Substitution

In zsh, parameters are set like this:

% foo=bar
% echo $foo
bar

Spaces before or after the = are frowned upon:

% foo = bar
zsh: command not found: foo

Also, set doesn’t work for setting parameters:

% 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.).

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

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

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

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

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

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

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

and a 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:

${(modifiers)parameter}

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

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

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

$ 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):

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

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

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

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

or an alias:

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

Another option that modifies parameter expansion is RCEXPANDPARAM:

% 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

[ prev | next | top ]