I'm running Debian Bullseye (if that matters), and I'm writing a zsh script to use rsync to copy multiple files/directories (passed as arguments to the script) from a given remote directory. They're all in the same directory on the server, but I will need to manually specify the names. So far, I have the following:



for f in "$@"
    files="servername:/full/path/'$f' $files"

rsync -rh --progress $files ~/Downloads/

This throws the error: rsync: change_dir "/home/username//servername:/full/path" failed: No such file or directory (2)

However, the first file listed in the arguments downloads successfully, after the above error is thrown for every other argument.

It seems like zsh is prepending the full path of the working directory to the arguments for some reason. Any idea why this is? Why it doesn't do that to the first argument? How to get around it? Or is there an easier way to copy multiple files from the same remote directory?

FYI, I can't just call rsync multiple times in the script because I need to enter a password for ssh every time, and I'd like this to be able to run over night. Also, the file/directory names sometimes have spaces and other special characters in them, which is why I put them in single quotes. The rsync command works when I type it in manually, but not when it is run from this script.

Thank you!

1 Answer 1


So, I figured it out. The answer was to use arrays. This works:



for f in "$@"
    files=($files "servername:/full/path/'$f'")

rsync -rh --progress ${files[@]} ~/Downloads/
  • 1
    You should use rsync's --protect-args rather that using those single quotes there which are unreliable. Oct 15, 2021 at 5:14
  • You can just write rsync -rh --protect-args --progress $files ~/Downloads/ in zsh. The ${files[@]} (or ${(@)files} or just $files[@]) is only useful when quoted and only to preserve empty elements (but here we're talking or arguments that can't be empty). Oct 15, 2021 at 5:16
  • You can do files+=(servername:/full/path/$f) to append an element to an array (or files+=servername:/full/path/$f but only if files was already an array), or files[1,0]=servername:/full/path/$f to prepend an element to an array Oct 15, 2021 at 5:19
  • Note that for loops over positional parameters by default (in all Bourne-like shells, not just zsh). So for i in "$@" can also be written for i Oct 15, 2021 at 5:20
  • 2
    Here the whole script could be written: rsync -rh --protect-args servername:/full/path/$^@ ~/Downloads/ Oct 15, 2021 at 5:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .