4

I have SSH executing some commands in a script, like so:

#!/bin/bash

REMOTEUSER=$1
REMOTEHOST=$2    
newVh=$3

ssh "$REMOTEUSER"@"$REMOTEHOST" << EOF

cd

if [ ! -d "/data/web/someDirectory" ]
then
  echo -e "There is no vhost setup for someDirectory"
  exit 1
fi


if [ ! -d "git" ]
then
  echo -e "Home git directory not found. Creating it now."
  mkdir git
fi

if [ ! -d "git/$newVh.git" ]
then
  echo -e "Bare git repository not found for $new. Creating it now."
  mkdir git/"$newVh".git
  git init --bare git/"$newVh".git
fi


EOF

Which works fine and does what I want. I'm trying to check if the compass ruby gem is installed on the remote machine and if it's not, I want to install it. I added this line to the bottom of the script:

.
.
.
if [ `gem list compass -i` == 'false' ]
then
  echo -e "Compass not installed... Installing it now."
  gem install compass -V
fi


EOF

But `gem list compass -i`. is getting evaluated on my local machine beforehand (I have compass installed). Even running gem list compass -i in the script with no quotes to see the output is returning true which is incorrect. I tried escaping the command with a number of \s but it didn't work.

I guess two questions:

How is it that other commands in the script (mkdir, cd, git init etc...) work correctly on the remote machine but gem list does not?

How do I escape that command to run it remotely?

Brandon
  • 411
  • 1
    You don't actually have dollar signs prefixing your variable assignments, do you? – Jeff Schaller Oct 19 '17 at 14:47
  • 1
    Yeah the leading dollar $PID_FILE=$(cat ...) error caused a massive and very expensive outage at some previous workplace of mine... (standard error was redirected and sent to /dev/null a bunch of times to better hide the error) – thrig Oct 19 '17 at 14:51
  • Do you really have EOF twice ? the << EOF will match only the first one..

    I would personally use rex (rexify.org) to execute staff on remote system(s) via ssh.

    – mestia Oct 19 '17 at 14:53
  • @JeffSchaller No. My bad. And the variable new isn't named new either. – Brandon Oct 19 '17 at 15:38

1 Answers1

4

The problem with your original code is that the command substitution expression (denoted by back-ticks) is evaluated prior to the execution of the ssh command. In a different situation you might consider quoting the here-doc keyword (e.g. "EOF") in order to prevent parameter expansion in your here-doc, but in this case you actually do want to perform some parameter substitution client-side. So a solution here could be to escape the command substitution. Try this instead:

if [ \`gem list compass -i\` == 'false' ]
then
  echo -e "Compass not installed... Installing it now."
  gem install compass -V
fi

The escaped back-ticks prevent the command substitution from being evaluated.

I'll also mention that the back-tick syntax for command substitution is widely considered to be a legacy feature and that the dollar-sign syntax is generally preferred nowadays. Here is what the above snippet would look like with the dollar-sign syntax instead:

if [ \$(gem list compass -i) == 'false' ]
then
  echo -e "Compass not installed... Installing it now."
  gem install compass -V
fi

For more information about the differences between the two notations, you can refer to the following StackExchange posts:

You may also want to look at the GNU Bash manual page on command substitution.

And also at the GNU Bash manual page on redirection.

Note in particular the following subsection on here-docs:

3.6.6 Here Documents

This type of redirection instructs the shell to read input from the current source until a line containing only word (with no trailing blanks) is seen. All of the lines read up to that point are then used as the standard input (or file descriptor n if n is specified) for a command.

The format of here-documents is:

[n]<<[-]word
    here-document
delimiter

No parameter and variable expansion, command substitution, arithmetic expansion, or filename expansion is performed on word. If any part of word is quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion, the character sequence \newline is ignored, and ‘\’ must be used to quote the characters ‘\’, ‘$’, and ‘`’.

If the redirection operator is ‘<<-’, then all leading tab characters are stripped from input lines and the line containing delimiter. This allows here-documents within shell scripts to be indented in a natural fashion.

igal
  • 9,886
  • 2
    You've gone back and forth between backticks and the $() form and I was hoping you'd leave it with the $() form. Unless you want to run scripts with the ca .1976 Bourne shell, there is no reason to still be using backticks. Other than that, +1. – NickD Oct 19 '17 at 16:16
  • @Nick Yeah, I've made a bunch of edits. I decided that it was poor form to use a different syntax than what was in the question without any warning, so I went back to the back-tick syntax that Brandon was using. But then I thought better of it and added some references regarding command substitution. Better now, I hope? – igal Oct 19 '17 at 16:29
  • Nice answer. I'll try the dollar sign syntax. I did try backslash escaping the backticks and it didn't work. – Brandon Oct 19 '17 at 19:06
  • Turned out to be that gem list -i wants a regex and was returning a false positive on installation of compass-core. The dollar sign worked perfectly. Thanks. – Brandon Oct 19 '17 at 19:16