The script below works in bash but not in zsh. I think it is because in the variable OPTS, I am "expanding" (not sure if this is the right word) the variable $EXCLUDE, and this syntax doesn't work in zsh. How would I replace that line to make it work on zsh?

OPTS="-avr --delete --progress --exclude=${EXCLUDE} --delete-excluded"                                               

rsync $OPTS $SRC $DST   

3 Answers 3


The problem here is that $OPTS is not split into several arguments on the rsync command line. In zsh syntax, use:

rsync ${=OPTS} $SRC $DST 

(an alternative is to simulate standard shell behavior globably with the option -o shwordsplit…)

From the manpage:

One commonly encountered difference [in zsh] is that variables substituted onto the command line are not split into words. See the description of the shell option SH_WORD_SPLIT in the section 'Parameter Expansion' in zshexpn(1). In zsh, you can either explicitly request the splitting (e.g. ${=foo}) or use an array when you want a variable to expand to more than one word. See the section 'Array Parameters' in zshparam(1).

  • 1
    Is there anyway I can make it portable to bash? I want to use the same script/function to be usable in both bash and zsh.
    – balki
    Sep 9, 2011 at 14:09
  • @balki: You can start your script with set -o shwordsplit 2>/dev/null to tell zsh to use sh word splitting. Or you can use arrays, as described by Gilles. Sep 9, 2011 at 14:43
  • Thanks a lot, and thanks for the quote from the man page Jan 18, 2020 at 17:38
  • Note that the solution of making OPTS an array (explained in Gilles's answer) is better than forcing word splitting, because it allows the options to contain word delimiters. If you force word splitting, an option like --include-from="filename with space" will be split into several words. Aug 9, 2020 at 8:09

The problem doesn't come from $EXCLUDE, it comes from $OPTS. Your script relies on word splitting happening to $OPTS. This is a bad idea; for example it will fail if you ever change $EXCLUDE to contain a pattern and --exclude=$EXCLUDE comes to match a file in the current directory, or if you ever change $EXCLUDE to contain whitespace.

$OPTS is a list of words, not a word. If you only need your script to work in ksh, bash and zsh, make it an array:

OPTS=(-avr --delete --progress --exclude="$EXCLUDE" --delete-excluded)
rsync "${OPTS[@]}" "$SRC" "$DST"

If you only intend the script to work in zsh, you can simplify the last line:

rsync $OPTS $SRC $DST

If you want the script to work in every shell, you need to use the positional parameters. They are the only array available.

set -- -avr --delete --progress --exclude="$EXCLUDE" --delete-excluded
rsync "$@" "$SRC" "$DST"

If you have a shell script that you want to be able to run conveniently with zsh, put this line at the beginning — it tells zsh to act like ksh, and it's a no-op on other shells.

emulate ksh >/dev/null 2>/dev/null
  • +1 for the pure zsh solution, as I came here looking to improve my oh-my-zsh profile.
    – Ekevoo
    Nov 2, 2019 at 2:15

To complete Stéphane Gimenez answer (I cannot comment yet), one solution is to make your script portable to bash by checking if you are using zsh and then transform $OPTS into an array like this

[ -n "${ZSH_VERSION:-}" ] && set -A OPTS ${=OPTS}

Then your rsync command will work.

  • What is the meaning of :- after ZSH_VERSION? Jan 3, 2020 at 12:45
  • 1
    @DmitriZaitsev Looks like the "Use default value" form of parameter expansion. In ${parameter:-word} if parameter is unset or null then then expansion of word is substituted. Otherwise the expansion of parameter is used. word can be omitted and that leaves an empty string in the above example. Evaluating -n against an empty (zero length) string results in false. So set won't be executed if you're not using zsh.
    – B Layer
    Jul 30, 2020 at 11:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.