3

I have a simple bash script; I basically want to make sure that the file exists on a remote machine. I've found numerous examples of how to do this, but the missing component is how to do this with spaces in the path and/or filename being evaluated.

#!/bin/bash

HOST=server.local
DIR=/foo/bar
FILE="Foo Bar File With Spaces"


if ssh $HOST [[ -f ${DIR}/${FILE} ]]
then
   echo "The file exists"
else
   echo "The file doesn't exist."
fi

So, it fails. It gets me a syntax error in conditional expression. However, if I change the FILE variable to say:

FILE="Foo\ Bar\ File\ With\ Spaces"

The script works (it finds the file since it's there).

I have tried the following variations to my conditional expression:

if ssh $HOST [[ -f "${DIR}/${FILE}" ]]

and

if ssh $HOST [[ -f "${DIR}"/"${FILE}" ]]

Neither of which work; I know that I am missing something simple. Can someone please point me in the right direction?

  • @Christopher It's just the way I learned to expand variables. Does it cause an issue? If I execute the command directly (not through the script) and type the path/filename out (ssh server.local [[ -f "/foo/bar/Foo Bar With Spaces" ]] && echo "File Exists" || "File Doesn't Exist") it still gives me the error – Allan Oct 19 '16 at 15:26
  • 2
    Warning: Using a variable named PATH is a really bad idea! You already have a PATH, if you change it you will face some unwanted consequences. – maulinglawns Oct 19 '16 at 15:30
  • @maulinglawns - True. I actually have the variables LDIR and RDIR defined in my script, but substituted PATH because it was easier to understand in the question...forgetting that there was already a PATH variable defined/declared. – Allan Oct 19 '16 at 15:34
  • 1
    Although not relevant here, get into the habit of never using upper case variable names. Both PATH and HOST are reserved variables. Since shell reserved variables are in upper case, the simplest way to never have a conflict is to make sure your own variables are always in lower case. That said, I would also advise you to use the more portable [ -f file ] instead of the bash (and a few other shell)-specific [[ -f file ]] when connecting to a remote machine. Don't assume the remote shell will be the same as your local one. – terdon Oct 19 '16 at 15:44
  • @terdon - Thanks for that. Just for the record (to save face a bit, too), my variable names were LDIR RDIR and NAS - I wrote the question with generic names. But I will start switching over to lowercase variables and will try to use more portable formats where I can. – Allan Oct 19 '16 at 15:50
8

Add an extra pair of quotes so there's one for the local shell and one for the remote shell that ssh runs.

$ dir=/tmp; file="foo bar"; 
$ ssh somewhere ls -l "'$dir/$file'"
-rw-r--r-- 1 foo foo 4194304 Oct 19 18:05 /tmp/foo bar
$ ssh somewhere [[ -f  "'$dir/$file'" ]] ; echo $?
0

You want the double-quotes on the outside so that the local shell expands the variables before the ssh command runs. With a single-quote on the inside, the remote shell won't expand special characters further.

Unless the file name contains single-quotes, that is. In which case you'll run into trouble.

To work around that, you'll need something that adds the necessary escapes to the string. Some systems have printf "%q", new versions of Bash have the ${var@Q} expansion which should do something similar similar.

$ dir=/tmp; file="\$foo' bar"
$ fullpath="$(printf "%q" "$dir/$file")"
$ ssh somewhere ls -l "$fullpath"
-rw-r--r-- 1 foo foo 0 Oct 19 18:45 /tmp/$foo' bar
  • That answer, right there...is "U&L StackExchange" gold. I didn't realize that it was expanding the variables after the ssh connection. – Allan Oct 19 '16 at 15:45
  • Note that this fails if one of the names contains a ' – Gilles Oct 19 '16 at 21:54
  • @Allan: to be exact, ssh passes the arguments it receives as a command to the shell on the remote system; that shell does ALL the things it normally does to a noninteractive command, usually brace tilde variable command and arithmetic expansions unless quoted (as sent), word splitting, and globbing unless disabled, as well as redirections (as sent). (But not history expansion because that's only for interactive.) – dave_thompson_085 Oct 20 '16 at 7:48
  • @Gilles, I think I mentioned the problem with single-quotes... – ilkkachu Oct 20 '16 at 9:13
  • @Allan, and it may be useful to expand some envvars like HOSTNAME, USER or SSH_* server-side. Actually you could pass them from the client to the remote side, but it requires a lenient AcceptEnv in the sshd configuration – ilkkachu Oct 20 '16 at 9:15
-1

I'm not sure I recommend this, but unless your server admin and/or their system/distro is zealous about security, you could abuse one of the (usually) few envvars ssh will pass through:

export LC_MESSAGES='path/to/file with spaces'
if ssh $host '[ -f "$LC_MESSAGES" ]' ; then ...
  • Better yet, if the server allows LC_*, we can use something that's actually not used by locale, e.g. LC_filename! Works on my Debian systems! Really, please no. This idea is so insane I'd want to give it some recognition, but on the other hand I don't really want anyone to see it. – ilkkachu Oct 20 '16 at 12:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.