gnivirht
gnivirht

Reputation: 315

Optionally pass an argument in a bash script

I would like to use custom identity file when using rsync, but only if the file exists, otherwise I don't want to bother with custom ssh command for rsync. I am having problems with quotes. See examples.

Desired command if identity file exists

rsync -e "ssh -i '/tmp/id_rsa'" /tmp/dir/ u@h:/tmp/dir

Desired command if identity file does not exist

rsync /tmp/dir/ u@h:/tmp/dir

I wanted to create a variable that would contain -e "ssh -i '/tmp/id_rsa'" and use it as follows

rsync ${identityArg} /tmp/dir/ u@h:/tmp/dir

This variable would be either empty or contain desired ssh command.

An example way I fill the variable (I have tried many ways)

IDENTITY_FILE="/tmp/id_rsa"
if [ -f "${IDENTITY_FILE}" ]; then
  identityArg="-e 'ssh -i \"${IDENTITY_FILE}\"'"
fi

The problem is that quotes are always wrong in the command and I end up with commands similar to these ones (set -x is set in the script and this is the output)

rsync -e '\ssh' -i '"/tmp/id_rsa"'\''' /tmp/dir/ u@h:/tmp/dir

There is something I do not get about quotation in bash. If you have any good resource about usage of single and double quotes in bash script I would like to read it.

Upvotes: 1

Views: 218

Answers (3)

dash-o
dash-o

Reputation: 14491

Trying to properly escape quotes is tricky. Better to try to leverage existing constructs. Few alternatives, depending on the situations

If the name of the identity file only contain simple characters (no spaces, wildcard, etc.) consider not wrapping it in quotes. In this case, you can

IDENTITY_FILE="/tmp/id_rsa"
if [ -f "${IDENTITY_FILE}" ]; then
  identityArg="-e 'ssh -i ${IDENTITY_FILE}'"
fi
...
rsync $identityArg ...

Another option is to always pass in the command (ssh or 'ssh -I ...'). This will automatically take care for special characters in the identity file.

IDENTITY_FILE="/tmp/id_rsa"
if [ -f "${IDENTITY_FILE}" ]; then
  identityArg="-i '${IDENTITY_FILE}'"
fi
rsync -e "ssh $identityArg" ...

Third alternative is to use array to create the arguments to rsync, and let the shell escape the characters as needed. This will allow any character in the identity file.

IDENTITY_FILE="/tmp/id_rsa"
if [ -f "${IDENTITY_FILE}" ]; then
  identityArg=(-e "ssh -i '${IDENTITY_FILE}'")
fi
rsync "${identityArg[@]}" ...

Upvotes: 0

Ivan
Ivan

Reputation: 7317

Try like this

id=/tmp/id_rsa
[[ -e $id ]] && o1='-e' o2="ssh -i '$id'"
echo $o1 "$o2" /tmp/dir/ u@h:/tmp/dir

Upvotes: 0

Benjamin W.
Benjamin W.

Reputation: 52536

You want to add two positional parameters: -e and ssh -i '/tmp/id_rsa', where /tmp/id_rsa is an expanded variable. You should use an array for this:

args=(/tmp/dir/ u@h:/tmp/dir)
idfile=/tmp/id_rsa

# Let [[ ... ]] do the quoting
if [[ -f $idfile ]]; then
    # Prepend two parameters to args array
    args=(-e "ssh -i '$idfile'" "${args[@]}")
fi

rsync "${args[@]}"

I'm not convinced the inner single quotes are necessary for ssh -i, but this expands to exactly the commands shown in the question.

Upvotes: 2

Related Questions