Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm trying to create a simple wrapper shell script for rsync. When I send file names to my script, rsync can never seem to identify the correct location. The file names have spaces in them. I've tried a dozen different variations using quotes, double quotes, backslashed quotes, and using the rsync -s --protect-args flag. I'm finally out of ideas. Here is a simplified version of my script.

# Example usage:
#    pull.sh "file 1" "file 2"

for file in "$@"; do
        all_files="$all_files:\"$LOCATION/$file\" "
# Pull the given files from homeserver to my current directory.
rsync --progress --inplace --append-verify -ave ssh username@homeserver"$all_files" .

Should I be writing this differently? How do make this script work?


I changed my script to try to reflect Chazelas's answer, but it still seems not to work. Here is my new code:

# Example usage:
#    pull.sh "file 1" "file 2"

for file in "$@"; do
    all_files="$all_files\"$LOCATION/$file\" "
rsync --progress --inplace --append-verify -0 --files-from=<(printf '%s\0' "$all_files") -ave ssh username@homeserver: .

Running it gives me the standard "usage" output, with this error at the end.

rsync error: syntax or usage error (code 1) at options.c(1657) [server=3.0.9]
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: error in rsync protocol data stream (code 12) at io.c(605) [Receiver=3.0.9]
share|improve this question
Stephane's answer should work. It is literally just a one liner, no need for the for loop, just use $@ directly in printf. – Graeme Jan 18 '14 at 2:10
up vote 1 down vote accepted

The problem is that you need to quote the file names, but you can't do all that using strings because it will pass all the file names through to rsync as one long string with quotes inside the string (and not individual file string parameters).

The variable $@ is an array in Bash. You need to convert it into a new array when sending to the rsync.

for file in "$@"; do
rsync --progress --inplace --append-verify -ave ssh username@homeserver"${all_files[@]}" .
share|improve this answer


# prepend "$location" to each element of the `"$@"` array:
for file do
  set -- "$@" "$location/$file"

rsync ... -0 --files-from=<(printf '%s\0' "$@") user@host: .


rsync ... -0 --files-from=<(
  for file do
    printf '%s\0' "$location/$file"
  done) user@host: .

to be on the safe side.

That passes the list of files as a NUL delimited list via a named pipe to rsync.

share|improve this answer
Thanks for your help Chazelas. I tried to implement your solution, but without success. I've updated the OP to explain. – Sepero Jan 18 '14 at 0:27
@Sepero, it's the "$@" array that's meant to be passed to printf here, see my edit if you want to prepend something to each element of that array. – Stéphane Chazelas Jan 18 '14 at 8:07

Since you are copying all the contents of WD100 to the current location, just let rsync sync the contents of the directory.

rsync --progress --inplace --append-verify -ave ssh username@homeserver:/media/WD100/ ./

rsync behaves differently depending on the trailing slash in the path. If there is no slash it copies the directory object recursively, but if there is a slash it will copy the contents of the directory recursively.

Here is the section of the rsync manual that is pertinent.

A trailing slash on the source changes this behavior to avoid creating an additional directory level at the destination. You can think of a trailing / on a source as meaning lqcopy the contents of this directoryrq as opposed to lqcopy the directory by namerq, but in both cases the attributes of the containing directory are transferred to the containing directory on the destination. In other words, each of the following commands copies the files in the same way, including their setting of the attributes of /dest/foo:

rsync -av /src/foo /dest
rsync -av /src/foo/ /dest/foo
share|improve this answer
I think there is a misunderstanding. I'm not "copying all the contents of WD100". The variable $all_files represents all the file names passed to the shell script, not all the files in WD100. – Sepero Jan 20 '14 at 12:13

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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