Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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]
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 at 2:10

3 Answers 3

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.

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[@]}" .
share|improve this answer

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.

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 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 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 at 12:13

Your Answer

 
discard

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.