expansion 

Send to Kindle
home » snippets » zsh » expansion



Parameter Expansion

Some common ones

From: Parameter Expansion

Syntax Description
{$name=word} if name is unset then set it to word
${name:=word} if name is unset or null then set it to word
${name::=word} unconditionally set name to word
{$name-word} if name is set, then substitute its value; otherwise substitute word
{$name:-word} if name is non-null, then substitute its value; otherwise substitute word
{$name+word} if name is set, then substitute word; otherwise substitute nothing
{$name:+word} if name is non-null, then substitute word; otherwise substitute nothing
{$name?word} if name is set, then substitute its value; otherwise, print word and exit from the shell
{$name:?word} if name is set and non-null, then substitute its value; otherwise, print word and exit from the shell

Initializing a variable to a default value when not set

: ${FOO:=/tmp/$BAR}

Replacing cat

The substitution ‘$(cat foo)’ may be replaced by the equivalent but faster ‘$(<foo)’

Find a specific parent directory

# Find the parent directory called "build"
echo ${PWD/build*\//build}

Expand indirectly / recursively – (P)

`${(P)E}`

Rescan and expand – (e)

A more powerful flag is (e), which forces the value to be rescanned for all forms of single-word substitution. For example,

% foo='$(print $ZSH_VERSION)'  
% print ${(e)foo}  
4.0.2

Word Splitting

# This forces word splitting while expanding E
${=E}

Split and Join

Use ps:X: and pj:X:.  The (f/F) flags are short for ps:\n: and pj:\n:.

# Read lines from a file into an array.
# Without the double quotes, you'll get just one long
# line with all the newlines replaced by spaces (which
# also prevents the splitting on newlines.)
lines=(${(f)"$(<file)"})  # The double quotes are needed.

# (F) does the opposite and joins items with a \n
lines=(one two three)
print ${(F)lines}  # prints one\ntwo\nthree

Quoting

Quote E when it is expanded using \'s. If you use (qq) instead, then use single quotes to quote it. And (qqq) specifies using double quotes. (qqqq) specifies using posix quotes.

${(q)E}

It's very common, however, that you want one line per argument, not splitting on spaces within the line. This is where parameter expansion can come in. There is a flag (f) which says split the result of the expansion, one word per line. Here's how to use it in this case:

$ args $(ls -l)  
4592

$ args "${(f)$(ls -l)}"  
490

$ text="$(ls -l)"  
$ args "${(f)text}"  
490

Splitting and Joining

Ref: Parameter Expansion Flags  (Search for Force field splitting at the separator string)

Syntax:

s:string:

Force field splitting at the separator string.  Note that a string of two or more characters means that all of them must match in sequence; this differs from the treatment of two or more characters in the IFS parameter.  See also the = flag and the SH_WORD_SPLIT option.  An empty string may also be given in which case every character will be a separate element.

NOTE:  For historical reasons, the usual behaviour that empty array elements are retained inside double quotes is disabled for arrays generated by splitting.  Override by using @s instead of s.  The following:

line="one::three"
print -l "${(s.:.)line}"

produces two lines of output for one and three and elides the empty field.  To override this behaviour, supply the (@) flag as well, i.e. ${(@s.:.)line}.

Example:

# split on commas
# prints A, B and C on 3 separate lines.
print_args ${(s:,:)${:-A,B,,C}}

# If we want the blank item, we must BOTH, add the @ flag as well as
# stick the whole thing in double quotes.
# prints A, B, empty string and C on 4 separate lines.
print_args "${(@s:,:)${:-A,B,,C}}"


# split on newlines
# prints A, B and C on 3 separate lines.
# NOTE: You need to use (ps:X:) and not (s:X:) if X contains
# escapes that you want interpreted (e.g. \n)
print_args ${(ps:\n:)${:-$'A\nB\nC'}}

Examples

Ref: http://grml.org/zsh/zsh-lovers.html#_unsorted_misc_examples