Check if a file exists on a remote machine with spaces in the filename and/or path

8,727

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
Share:
8,727

Related videos on Youtube

Allan
Author by

Allan

Smarter Cities and Cloud Consultant I got my start in Novell right out of high school and then cut my teeth on Sun Solaris. I am platform agnostic - meaning I use the right tool for the job. I have all flavors of operating systems macOS, Windows, and Unix. My personal stable of hardware consists of an iMac, a MacBook Pro, a Surface Pro 3, Dell Precision workstations and a Mac Pro I am attempting to refurbish - and I love them all. I much prefer FreeBSD over Linux (that's the Solaris in me) and of course OS X. My specialties are in virtualization and cloud computing. When I was working for Sun, I was doing "cloud" before it was called cloud; we called it "utility computing." I have over 25 years experience doing this, so if there's anything I can assit with, please feel free to reach out. You can reach me at the following: Email & Skype: [email protected]

Updated on September 18, 2022

Comments

  • Allan
    Allan over 1 year

    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?

    • Allan
      Allan over 7 years
      @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
    • maulinglawns
      maulinglawns over 7 years
      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.
    • Allan
      Allan over 7 years
      @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.
    • terdon
      terdon over 7 years
      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.
    • Allan
      Allan over 7 years
      @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.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 7 years
    Note that this fails if one of the names contains a '
  • dave_thompson_085
    dave_thompson_085 over 7 years
    @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.)
  • ilkkachu
    ilkkachu over 7 years
    @Gilles, I think I mentioned the problem with single-quotes...
  • ilkkachu
    ilkkachu over 7 years
    @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
    ilkkachu over 7 years
    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.