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:
SRC="/path_to_source"
DST="/path_to_dest"
EXCLUDE=".hg"
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