5

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.

#!/bin/bash
# Example usage:
#    pull.sh "file 1" "file 2"

LOCATION="/media/WD100"
all_files=""
for file in "$@"; do
        all_files="$all_files:\"$LOCATION/$file\" "
done
# 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?

UPDATE:

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

#!/bin/bash
# Example usage:
#    pull.sh "file 1" "file 2"

LOCATION="/media/WD100"
all_files=""
for file in "$@"; do
    all_files="$all_files\"$LOCATION/$file\" "
done
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]
1
  • Stephane's answer should work. It is literally just a one liner, no need for the for loop, just use $@ directly in printf.
    – Graeme
    Commented Jan 18, 2014 at 2:10

3 Answers 3

5

Use:

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

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

Or:

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.

2
  • Thanks for your help Chazelas. I tried to implement your solution, but without success. I've updated the OP to explain.
    – Sepero
    Commented Jan 18, 2014 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. Commented Jan 18, 2014 at 8:07
1

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.

LOCATION="/media/WD100/"
all_files=()
for file in "$@"; do
    all_files+=(":\"$LOCATION$file\"")
done
rsync --progress --inplace --append-verify -ave ssh username@homeserver"${all_files[@]}" .
0

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
1
  • 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
    Commented Jan 20, 2014 at 12:13

You must log in to answer this question.

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