1
votes

I'm trying to create a bash script which will sync a directory specified as a command line parameter to a remote server (also specified by a parameter). At the moment, I'm using eval, which solves a parameter expansion problem, but for some reason causes rsync not to preserve ownership on the remote files (apart from being Evil, I know). Running the rsync command with all the same flags and parameters from the command prompt works fine.

I tried using $() as an alternative, but I got into a real mess with variable expansion and protecting the bits that need protecting for the remote rsync path (which needs both quotes and backslashes for paths with spaces).

So - I guess 2 questions - is there a reason that eval is preventing rsync from preserving ownership (the bash script is being run as root on the source machine, and sshing to the remote machine as root too - just for now)? And is there a way of getting $() to work in this scenario? The (trimmed) code is below:

#!/bin/bash

RSYNC_CMD="/usr/bin/rsync"
RSYNC_FLAGS="-az --rsh=\"/usr/bin/ssh -i \${DST_KEY}\""  # Protect ${DST_KEY} until it is assigned later

SRC=${1}  # Normally this is sense checked and processed to be a canonical path

# Logic for setting DST based on command line parameter snipped for clarity - just directly assign for testing

DST='[email protected]:'
DST_KEY='/path/to/sshKey.rsa'

TARG=${DST}${SRC//' '/'\ '}  # Escape whitespace for target system

eval ${RSYNC_CMD} ${RSYNC_FLAGS} \"${SRC}\" \"${TARG}\"  # Put quotes round the paths - even though ${TARG} is already escaped

# All synced OK - but ownership not preserved despite -a flag

I've tried changing RSYNC_CMD to sudo /usr/bin/rsync, and also adding --rsync-path="sudo /usr/bin/rsync to RSYNC_FLAGS, but neither made any difference. I just can't see what I'm missing...

1
rsync --help | grep owner tells me: -o, --owner preserve owner (super-user only). Did you try that option? - Bobby
Thanks - as @chepner said below, -a implies -o, and if I do it from the command line, it works fine, so I'd presumed it was the eval (just to be sure, I did try -o, but no difference). I'm going to give chepner's rewrite a go in the next few days. - dsl101

1 Answers

2
votes

The correct way to do this is to use an array. -a should already imply -o.

RSYNC_CMD="/usr/bin/rsync"

DST='[email protected]:'
DST_KEY='/path/to/sshKey.rsa'

RSYNC_FLAGS=(-az --rsh="/usr/bin/ssh -i ${DST_KEY}")

SRC=${1}
TARG="${DST}$SRC"

${RSYNC_CMD} "${RSYNC_FLAGS[@]}" "${SRC}" "${TARG}"

Using RSYNC_RSH instead of --rsh, you can export the variable before you set its value. This at least lets you put the export in the same area where you set the rest of the flags. Then you can defer completing its value until after you have the correct identity file.

RSYNC_CMD="/usr/bin/rsync"
export RSYNC_RSH="/usr/bin/ssh -i %s"  # Use a placeholder for now; set it later
RSYNC_FLAGS=( -a -z )

# Later...

DST='[email protected]:'
DST_KEY='/path/to/sshKey.rsa'
RSYNC_RSH=$( printf "$RSYNC_RSH" "$DST_KEY" )


SRC=${1}
TARG="${DST}$SRC"

${RSYNC_CMD} "${RSYNC_FLAGS[@]}" "${SRC}" "${TARG}"