2

I'm trying to test if a file in $SSHPATH/$ID.* exists. I'm using sshpass to supply a password so I don't have to type.

I want to execute a code if file doesn't exist

if [ "sshpass -p $SSHPASS ssh root@$SSHHOST test ! -f $SSHPATH/$ID.*" ]; then

This seems to return true even if the file exists on the remote server. Why?

I don't think it's a duplicate of How to conditionally do something if a command succeeded or failed because this also doesn't work, it didn't execute if the file doesn't exist

if sshpass -p "$SSHPASS" ssh "root@$SSHHOST" test ! -f "$SSHPATH/$ID.*"; then
Freedo
  • 1,255

2 Answers2

3

As pLumo explained, your test will always succeed.

What you want to do is to test whether your pattern, $SSHPATH/$ID.*, expands to something. This is different from testing with -f whether it's a regular file, as the pattern may expand to multiple names and the -f test can only be applied to a single pathname.

Locally, you can do this with

set -- "$SSHPATH/$ID".*
if [ -e "$1" ]; then ...; fi

This would set the positional parameters ($1, $2, etc.) to the names matching the pattern given to set. The if statements then tests whether the first match is an existing name (the pattern would remain unexpanded in most shells if it did not match anything).

To run this through ssh:

ssh user@host "set -- '$SSHPATH/$ID'.*; [ -e \"\$1\" ]"

The wonky quoting ensures that the $SSHPATH and $ID is expanded by the local shell, while "$1" is expanded remotely.

This would give you an exit status of zero ("success") if the pattern matched an existing name, and non-zero ("failure") if it did not (or if ssh failed in some manner).

You may use this in an if statement like so:

if ssh user@host "set -- '$SSHPATH/$ID'.*; [ -e \"\$1\" ]"; then
   # do something for success
else
   # do something for failure
fi

To test for non-existence, negate the test:

if ssh user@host "set -- '$SSHPATH/$ID'.*; [ ! -e \"\$1\" ]"; then
   # do something for failure
else
   # do something for success
fi

I have ignored your use of sshpass, but you may possibly just prefix the ssh with it as you have done in the question.

Kusalananda
  • 333,661
  • Ok I found the reason of the strange issue, I had to first connect with ssh, so it would ask me to save the ECDSA key fingerprint. So maybe add that to your answer, to remember people that they have to connect at least once manually...This is interesting because say you want to run a script that ssh into a remote server but you never connected to it before and you don't/can't to... – Freedo Aug 08 '19 at 09:42
  • What if I want to match two extensions, but only run if none exist? I tried '$SSHPATH/$ID'*.{doc,txt} but then it would execute as long one of them is missing, which isn't what I want. If either $ID.txt or $ID.doc is present, then don't run the if, only if both are missing... – Freedo Aug 08 '19 at 09:57
  • @Freedo Use shopt -s nullglob; set -- .... – Kusalananda Aug 08 '19 at 10:01
  • Just nullglob at the top of script doesn't work, if I put before set... then it fails with No such file or directory – Freedo Aug 08 '19 at 10:07
  • @Freedo Is the remote shell not bash maybe? Did you put a ; between shopt -s nullglob and the set? – Kusalananda Aug 08 '19 at 10:08
  • It is bash, maybe I'm doing wrong... shopt -s nullglob && "set -- '$SSHPATH/$ID'*.{txt,doc} – Freedo Aug 08 '19 at 10:09
  • @Freedo That's not what I wrote. – Kusalananda Aug 08 '19 at 10:10
  • @Kusalanada, shopt -s nullglob; "set -- '$SSHPATH/$ID'*.{txt,doc}; fails with no such file... I never thought this would be so complex :/ – Freedo Aug 08 '19 at 10:16
  • 1
    @Freedo I know. I said shopt -s nullglob; set -- .... The shopt needs to be within the double quotes. – Kusalananda Aug 08 '19 at 10:17
  • wow...you must have some decades of unix to know all of this! Thanks so much for the time and patience! Simple concepts can go complex so fast. I'll leave this here as can be useful for others – Freedo Aug 08 '19 at 10:19
  • @Freedo Only 3 decades. – Kusalananda Aug 08 '19 at 10:21
  • This is weird. The if statement is breaking my while loop. How is that possible? if I do mysql -h $DBHOST -P $DBPORT $DBNAME -e "SELECT id FROM files" -u $DBUSER -p$DBPASS | while read ID; do echo $ID; done it loops over the IDs, if I put the if inside the loop, it doesn't work. even putting echo after the IF will only print the first match – Freedo Aug 08 '19 at 12:21
  • 1
    @Freedo ssh inherits the standard input of the loop, so it eats the rest of the data from mysql. You have to use ssh with its -n option. – Kusalananda Aug 08 '19 at 12:33
  • sorry to bother you again, but my ssh command sometimes fail with ssh_exchange_identification: Connection closed by remote host maybe because i'm spamming too much, I've tried adding -o 'ConnectionAttempts 10' but it doesn't seem to retry at all...do you know why? – Freedo Aug 17 '19 at 10:55
  • @Freedo You should ask a new question about that. I rarely handle follow-up questions in comments. You may obviously search the site first before asking that new question. – Kusalananda Aug 17 '19 at 10:59
1

Your first command is the same as

if [ "some string" ]; then ... fi

which will always be true.

The second command is almost right, but you need to put the ! inside quotes or escape it, otherwise it will be interpreted by your shell.

Double quote the whole command and single quote the path to avoid issues with special characters inside the path. Make sure, to have the * outside the single quotes:

if sshpass -p "$SSHPASS" ssh root@$SSHHOST "test ! -f '$SSHPATH/$ID.'*"; then ... fi

If your pattern matches multiple files, the test will fail.

pLumo
  • 22,565
  • Still not working for me, it doesn't run even if the file don't exist. – Freedo Aug 08 '19 at 08:13
  • What do you mean? This is what I see on my terminal, with bash -x + sshpass -p redacted ssh root@redacted 'test \! -f '\''/home/x/y/z/6376.*'\''' – Freedo Aug 08 '19 at 08:22
  • I did thought about that, and tried some variations too, still not running with your last edit :( I don't know why – Freedo Aug 08 '19 at 08:24
  • Note that the test will be malformed if the pattern matches multiple files. – Kusalananda Aug 08 '19 at 08:27
  • There isn't any file starting with 6376 in the directory, I don't understand why it's not running :/ It doesn't fail, it just don't run – Freedo Aug 08 '19 at 08:31
  • maybe you should check your path. Is it an absolute path? – pLumo Aug 08 '19 at 08:39