0

I'm making a script to synchronize directories with rsync over ssh. I come into trouble when I want to define a custom port. Suppose a normal working script would have a syntax:

#! /bin/sh

rval=2222
port="ssh -p $rval"

rsync --progress -av -e "$port" sflash@192.168.10.107:/home/sflash/Documents/tmp/tcopy/ /home/sflash/Documents/tmp/tcopy

the syntax when disclosing a custom port is -e "ssh -p 2222". However, if I want to use a variable in this case like:

#! /bin/sh

rval=2222
port="-e \"ssh -p $rval\""

rsync --progress -av $port sflash@192.168.10.107:/home/sflash/Documents/tmp/tcopy/ /home/sflash/Documents/tmp/tcopy

This will not work likely due to some sort of interaction with IFS. I can completely avoid this scenario if I introduce an if statement to check if port is defined, but I am curious on the exact reason why this fails and if a solution exists to forcefully implement this method.

EDIT: sorry I am restricted to just posix shell

Silver Flash
  • 871
  • 3
  • 7
  • 16

1 Answers1

1

You haven't actually provided enough detail to be certain, but I suspect you are hitting a common misconception.

When you do:

rval=2222
rsync --progress -av -e "ssh -p $rval" src dst

rsync is invoked with 6 arguments: --progress, -av, -e, ssh -p 2222, src, and dst.

On the other hand, when you do:

port="-e \"ssh -p $rval\""
rsync --progress -av $port src dst

rsync is invoked with 8 arguments: --progress, -av, -e, "ssh, -p, 2222", src, and dst.

You do not want the double quotes to be passed to rsync, and you do not want the ssh -p 2222 to be split up into 3 arguments. One (terrible) way to do what you want is to use eval. But it seems what you really want is:

rval=2222
port="ssh -p $rval"
rsync --progress -av ${port:+-e "$port"} src dst

Now, if port is defined and not the empty string, rsync will be invoked with the additional arguments -e and ssh -p 2222 (as desired), and if port is undefined or empty, neither the -e nor the $port argument will be used.

Note that this is a case where you must not use double quotes around ${port:+-e "$port"}. If you do so, then an empty string would be passed as an argument when $port is the empty string. When $port is not the empty string, it would pass a single argument -e ssh -p 2222 rather than splitting into 2 arguments -e and ssh -p 2222.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • It works, thank you. I'm keeping on using parameter expansion way just so that I won't forget how shell expands my arguments – Silver Flash Dec 27 '20 at 13:14