0

I'm trying to write a bash script for a snapshot backup with rsync to a server.

The command I want to invoke is

rsync -aPh -e ssh --rsync-path='sudo rsync' --link-dest=../last /home/username/files remoteuser@server:/srv/backups/snapshots/username/snapshot_xxx

my script looks like

OPT="-aPh -e ssh --rsync-path='sudo rsync'"
LINK="--link-dest=../last/" 
SRC="/home/username/files/"
SNAP="remoteuser@server:/srv/backups/snapshots/username/"
date=`date "+%Y-%b-%d_%T"`

# Run rsync to create snapshot
rsync $OPT $LINK $SRC ${SNAP}$date

running this script with bash -x the command I'm getting is

rsync -aPh -e ssh '--rsync-path='\''sudo' 'rsync'\''' --link-dest=../last /home/username/files remoteuser@server:/srv/backups/snapshots/username/snapshot_xxx

which leads to the following errors:

bash: -c: line 0: unexpected EOF while looking for matching `''

bash: -c: line 1: syntax error: unexpected end of file

How do I prevent bash from inserting the extra single quotes and escaped single quotes?

peter
  • 13
  • Can you please share exactly how you are running the script? normally, -c would not be used – steeldriver Nov 03 '19 at 19:44
  • I'm just typing in the name of the script which is located in /usr/local/bin in that case I enter snapbackupremote.sh for the output I showed in my question i did run bash -x /usr/local/bin/snapbackupremote.sh – peter Nov 03 '19 at 19:46
  • sorry copy/paste error since i use the script local as well, i have corrected the question – peter Nov 03 '19 at 20:03

1 Answers1

2

Bash doesn't insert extra quotes, you did, here:

OPT="-aPh -e ssh --rsync-path='sudo rsync'"
                              ^          ^

That assignment sets OPT to the string -aPh -e ssh --rsync-path='sudo rsync'. When that's expanded in the rsync command line, it gets split on the whitespace to five different arguments: aPh, -e, ssh --rsync-path='sudo and rsync'.

The '\'' things you see in the output of bash -x are a representation of those arguments containing quotes . It's a bit hard to read, but 'foo'\''bar' is the single-quoted equivalent of "foo'bar" (it has two single-quoted strings, and an escaped single quote in the middle.)

Quotes don't work after a variable is expanded, you can't store a command with arguments containing whitespace in a simple variable, you'll need to use an array instead.

See:

ilkkachu
  • 138,973
  • reading the two articles you metntioned in your answer, I can't figure out which syntax i need to use to get the correct syntax for my command. The call needs to be --rsync-path='sudo rsync' the single quotes are needed, the best thing i can come up with is using an array like OPT=(-aPh -e ssh '--rsync-path='''sudo rsync'') i tried different variants none worked, I am calling the function with rsync ${OPT[@]} $LINK $SRC ${SNAP}$date, maybe you can tell me the correct syntax for this very case – peter Nov 03 '19 at 21:15
  • @peter, I don't think you need to pass the quotes to rsync. When you write rsync --rsync-path="sudo rsync" on the shell command line, what the quotes do is tell the shell that the space is supposed to be part of the string passed to rsync. What rsync sees is just the argument --rsync-path=sudo rsync. It knows how to remove the part up to the equal sign and treat the rest as the command. In an array, you'd pass that as ( ... --rsync-path="sudo rsync" ... ), or ( ... '--rsync-path=sudo rsync' ... ) or whatever. – ilkkachu Nov 03 '19 at 21:24
  • '--rsync-path='\''sudo rsync'\' would pass the quotes to rsync, which then passes the command 'sudo rsync' to the shell, which would then look for a command called sudo rsync in the path. (That's different from running the command sudo with an argument of rsync, which would happen if the shell just got the command sudo rsync). For example, on my machine, rsync -av '--rsync-path="sudo rsync"' -e ssh y localhost:/tmp/z gives the error bash: sudo rsync: command not found. – ilkkachu Nov 03 '19 at 21:29
  • @ikkachu comment1 neither OPT=(-aPh -e ssh '--rsync-path=sudo rsync') nor OPT=(-aPh -e ssh --rsync-path="sudo rsync") does work, this yields to an error: sudo: unrecognized option '--server', the same thing happens if I run the command manually. However running the command manually with either single or double quotes around sudo rsync does work – peter Nov 03 '19 at 21:40
  • @ikkachu comment2 passing sudo rsync works just fine, this runs rsync on the server with sudo, which I need since the directory I need to backup to is only writeable by root, and on the remote machine i allow the remoteuser to run rsync via sudo without a password remotesuer ALL=(ALL) NOPASSWD: /usr/bin/rsync. Running this command rsync -aPh -e ssh --rsync-path='sudo rsync' --link-dest=../last /home/username/files remoteuser@server:/srv/backups/snapshots/username/snapshot_xxxon my machnie just works fine but i can't get it to run from a script – peter Nov 03 '19 at 21:46
  • @peter, yes, I rechecked the manual, rsync passes the argument to --rsync-path through a shell, so yeah, sudo rsync works there. (That just makes the option oddly named, but nevermind that.) – ilkkachu Nov 03 '19 at 21:47
  • It looks to me that you're missing the quotes around the array expansion. You need rsync "${OPT[@]}" ..., not rsync ${OPT[@]} .... Without the quotes, the contents of the array get word-split again, and then it's the same as running rsync ... --rsync-path=sudo rsync ... on the command line (that is, the sudo and rsync aren't part of the same argument) – ilkkachu Nov 03 '19 at 21:50
  • Thank you, I appreciate your help. The quotes around the array expression finally solved the problem – peter Nov 03 '19 at 22:00