Help Shell script to pass variables to rsync

8,263

Solution 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[@]}" .

Solution 2

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:
8,263

Related videos on Youtube

Sepero
Author by

Sepero

Updated on September 18, 2022

Comments

  • Sepero
    Sepero over 1 year

    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]
    
    • Admin
      Admin over 10 years
      Stephane's answer should work. It is literally just a one liner, no need for the for loop, just use $@ directly in printf.
  • Sepero
    Sepero over 10 years
    Thanks for your help Chazelas. I tried to implement your solution, but without success. I've updated the OP to explain.
  • Stéphane Chazelas
    Stéphane Chazelas over 10 years
    @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.
  • Sepero
    Sepero over 10 years
    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.