Yet more Bash syntax help

Posted by: tfabris

Yet more Bash syntax help - 23/04/2019 05:10

Hey friends. I've been bashing my head against this one for a while, including doing a lot of googling and searching of StackOverflow. I'm hoping one of you kind folks can help with some expertise.

I have an "If" statement containing a "Find" statement within it, which works perfectly as long as the file being "found" doesn't reside a folder that contains spaces in the path name. As soon as spaces get in the mix, then it gives me an error: "Unary Operator Expected".

Here is an example script. There are two versions of the testFile variable. Depending on which one I uncomment, the script either works as expected, or it throws "Unary Operator Expected". (Note: A copy of the file exists in both places for the purposes of this test, both in the unspaced location and the spaced location.)

I've been trying various quoting options for the variable and none are working to improve the script. I've read through this but I must be missing something. Also, this may be a factor: I'm doing this at the Mac OS X shell, which has Bash version 3 instead of version 4.

Any ideas of what the correct syntax of that "find" statement should be?

Code:
#!/bin/bash

# This works as expected:
testFile="/Users/tonyfabris/Documents/test-file"

# This produces the error "Unary Operator Expected"
# when it hits the "find" command because of the
# space in the path name:
testFile="/Users/tonyfabris/Documents/Test Folder/test-file"

# Ensure the file is present. Works with both versions of the files.
if [ ! -f "$testFile" ]
then
  echo "Test file not found. Exiting."
  exit 1
fi
echo "Test file is present. Continuing program."

# Test the age of the file and only perform an operation
# if the file is more than 18 hours old.
if [ $( find "$testFile" -mmin +1080 ) ]
then
  echo "File is more than 18 hours old. Continuing with program."
else
  echo "No action will be performed based on the file age."
  exit 1
fi
echo "Performing action on the file."
Posted by: tfabris

Re: Yet more Bash syntax help - 23/04/2019 05:13

I just realized I punned unintentionally, but now I know why they call it "bash".
Posted by: Shonky

Re: Yet more Bash syntax help - 23/04/2019 07:58

The problem is that when your file is older than 18 hours your find command returns the filename not a true/false.

So you end up with

Code:
if [ /Users/tonyfabris/Documents/Test Folder/test-file ]


which has two "parameters" hence the error. When there's no space bash seems to return true for just a single parameter

When find doesn't match the file it returns "null" i.e.

Code:
if [ ]; then


Quick fix (I'm no bash expert). Something like:

Code:
#!/bin/bash

# This works as expected:
testFile="/build/christian/tmp/nospace-file"

# This produces the error "Unary Operator Expected"
# when it hits the "find" command because of the
# space in the path name:
testFile="/build/christian/tmp/space file"

# Ensure the file is present. Works with both versions of the files.
if [ ! -f "$testFile" ]
then
  echo "Test file not found. Exiting."
  exit 1
fi
echo "Test file is present. Continuing program."

# Test the age of the file and only perform an operation
# if the file is more than 18 hours old.
if [ "$( find "$testFile" -mmin +1080 )" != "" ]
then
  echo "File is more than 18 hours old. Continuing with program."
else
  echo "No action will be performed based on the file age."
  exit 1
fi
echo "Performing action on the file."
Posted by: mlord

Re: Yet more Bash syntax help - 23/04/2019 14:00

Why are you even using the "find" command in there? "find" is for locating files when you are uncertain of the exact path. But in the example here, at least, you do know the exact path. So what are you trying to have it do?

Checking for existence of a known pathname is easy:

if [ -e "$testfile" ]; then echo "it lives!" ; else echo "not there" ; fi

Also, the syntax used there for "find" doesn't make full sense to me. The "find" command takes a directory as a parameter, and searches within it for "files" which match any other parameters you supply based on name, date, size, etc.. But it doesn't look as it is being used correctly here

Puzzled! smile
Posted by: canuckInOR

Re: Yet more Bash syntax help - 23/04/2019 15:55

He's using it as a sort of filter -- if the file is less than 18hrs old, the find command filters out the filename.
Posted by: canuckInOR

Re: Yet more Bash syntax help - 23/04/2019 16:04

Perhaps a more canonical approach would be using date and stat:

Code:
cutoff=$((`date +%s`-64800))
age=`stat -c %Y "$testfile"`
# or, if your date command supports it:
# age=`date -r "$testfile" +%s`
if [ $age -gt $cutoff ]
then
    echo "File is too new."
    exit 1
fi
echo "File is over 18 hours old."

Posted by: tfabris

Re: Yet more Bash syntax help - 23/04/2019 17:48

Shonky,

Thank you so much for that brilliant answer.

The reason that I was bashing my head against it was because I was convinced that the problem was with the syntax or the string-quoting of the variable being passed into the find command. I hadn't considered the possibility of the results coming back out of the command to be the root cause. Very cool, thank you.

Mark,

You're correct that it looks weird to use "find" to test the age of a file. After googling for the simplest and most reliable way to do that task, everyone on the internet seemed to agree that using the "-mmin" parameter of the find command was the best way. Looking at the built-in parameters to "find", it seems that indeed it's designed to do this very thing, and it does it quite well. The only issue with that method is that a true/false test must be done the way Shonky shows it, not the way I saw it in the first example I found on StackOverflow. Clearly the StackOverflow answer-er didn't test their example code with spaces in the path name.

Canuck,

Thank you so much for the alternative method of testing the file age. That is certainly a more direct way to do the test. I had preferred the simplicity of using the "find" command as a one-liner because the syntax was cleaner.
Posted by: Roger

Re: Yet more Bash syntax help - 24/04/2019 11:05

Originally Posted By: tfabris
spaces in the path name


People with a long history of using Unices don't use spaces in file names, because they break so many tools.
Posted by: mlord

Re: Yet more Bash syntax help - 24/04/2019 14:01

Originally Posted By: Shonky

Code:
# Ensure the file is present. Works with both versions of the files.
if [ ! -f "$testFile" ]
then
  echo "Test file not found. Exiting."
  exit 1
fi
echo "Test file is present. Continuing program."


Note that the test above does not seem to be necessary given the code changes suggested further on, and in fact also serves to introduce a race condition into the script: the file could be removed or created elsewhere between the two tests. In this case, the race doesn't matter (really!), but it also demonstrates that the pre-test for existence above isn't as useful as one might think.

It's really a shame that "find" doesn't return a non-zero exit status for cases where nothing is found. Doing so could simplify scripts like this one, but would also break many others.

Cheers
Posted by: tfabris

Re: Yet more Bash syntax help - 24/04/2019 22:03

Quote:
People with a long history of using Unices don't use spaces in file names, because they break so many tools.


It took me a long time to get comfortable with spaces in file names on Windows, for similar reasons. And there too, I still sometimes find spaces break certain tools, or require the tool to be edited.


Quote:
Note that the test above does not seem to be necessary given the code changes suggested further on, and in fact also serves to introduce a race condition into the script


Good point. The actual code where I'm using this is structured differently, and so the race wouldn't exist. I put it into the example script, just to prove to myself that I wasn't crazy; that the file exists, and the quoting works for that file name in an "if" statement.
Posted by: jmwking

Re: Yet more Bash syntax help - 27/04/2019 01:14

Yeah. No spaces here. Just underscores when appropriate/unavoidable.

-jk