2

I am attempting to check if a specific file exists (but only if less than 24 hours old), and if so, perform a second check to see if a file with a name ending in ".control" exists. If both of these conditions are met then create an email and log entry. If the first file exists but the .control does not, then make a different log entry. If the file less than 24 hours old doesn't exist, then continue with the rest of the script.

This is in a shell script launched from cron using Bash on Solaris and Linux.

if [ find "$MAINTENANCE_LOCK/testLOCK" -mtime -1 ]; then
    if [ -f "$PATH_TO_TRIGGER_FILES/*.control" ]; then
        test_log_msg "test_SYSTEM" $NOTICE "Valid Maintenance Lock file found, test actions blocked." "status"
        test_log_email " " " " " " "test Actions were blocked because an testLOCK file less than 24hrs old is in place." " "
    else
        test_log_msg "test_SYSTEM" $NOTICE "Valid Maintenance Lock file in place." "status"
        exit
    fi
fi

However when I step through the lines of the code with "set -x trap read debug" I get the following error:

+ '[' find /var/opt/test/maintenancelock/testLOCK -mtime -1 ']'
/opt/test/test_control.sh: line 72: [: too many arguments

Is there a way to do what I want here?

3
  • 2
  • 3
    I think maybe you want something like if [ -n "$(find "$MAINTENANCE_LOCK/testLOCK" -mtime -1)" ] ; then ... Putting find directly inside [...] doesn't really make any sense.
    – frabjous
    Oct 18, 2022 at 22:05
  • 3
    if [ -f "$PATH_TO_TRIGGER_FILES/*.control" ] probably isn't what you want either, unless the file literally should have an asterisk in its name. You could do something similar to the other conditional. if [ -n "$(find "$PATH_TO_TRIGGER_FILES" -name '*.control')" ] ; then
    – frabjous
    Oct 18, 2022 at 22:12

3 Answers 3

5

Try help [ then help test -- [ is a synonym for test, and the test help says:

The behavior of test depends on the number of arguments.

With this: [ find "$MAINTENANCE_LOCK/testLOCK" -mtime -1 ];
you're passing 4 words to [, and it doesn't know what to do with 4 arguments.


I am attempting to check if a specific file exists (but only if less than 24 hours old), and if so, perform a second check to see if a file with a name ending in ".control" exists.

With bash:

mapfile -t testlock < <(find "$MAINTENANCE_LOCK/testLOCK" -mtime -1)
if [[ ${#testlock[@]} -eq 0 ]]; then
    echo "lock file does no exist or is too old"
else
    control=( "$PATH_TO_TRIGGER_FILES/"*.control )
    if [[ ${#control[@]} -gt 0 ]]; then
        echo "lock file exists and at least one control file exists"
    else
        echo "lock file exists but no control files exist"
    fi
fi
4
  • 1
    You can devise valid expressions for test with exactly 4 arguments, but you'd need -a or -o to combine 2 expressions. Oct 18, 2022 at 22:52
  • I tried to implement this solution and I get the following error on Solaris: Sending status message to user grep: illegal option -- o grep: illegal option -- E Usage: grep [-c|-l|-q] -bhinsvw pattern file . . .
    – axeman1984
    Oct 19, 2022 at 14:45
  • I don't see grep in my answer. Maybe you need to ask a separate question. Oct 19, 2022 at 15:02
  • Technically, [ find "..." -mtime -1 ]; passes 5 words to [, namely find, "...", -mtime, -1, and ].
    – A. R.
    Oct 19, 2022 at 15:04
1

By default, find always returns true (0), whether it finds a matching file or not.

However, using the -exec predicate to run /bin/false on matching file(s), you can get it to return false (1) if it finds one or more matches. Testing the negation (!) of that will be true if found, false otherwise.

For example:

# does the lock file exist?
if ! find "$MAINTENANCE_LOCK/testLOCK" -mtime -1 -exec false {} + ; then
  # are there any .control files?
  if ! find "$PATH_TO_TRIGGER_FILES" -name '*.control' -exec false {} + ; then
    test_log_msg "test_SYSTEM" $NOTICE "Valid Maintenance Lock file found, test actions blocked." "status"
    test_log_email " " " " " " "test Actions were blocked because an testLOCK file less than 24hrs old is in place." " "
  else
    test_log_msg "test_SYSTEM" $NOTICE "Valid Maintenance Lock file in place." "status"
    exit
  fi
fi

If you do this a lot, or just want your script to be more-readable/self-documenting, you could wrap it in a function:

file_exists () {
  # The args to this function are passed directly to find,
  # so anything that works with find will work here.
  # And anything that breaks find will break this function too.
  # Also, you probably don't want to use any find arguments that
  # produce any output.

  if ! find "$@" -exec false {} + ; then
    # one or more matching files were found, return true
    return 0
  else
    # nothing found, return false
    return 1
  fi
}

if file_exists "$MAINTENANCE_LOCK/testLOCK" -mtime -1 ; then
  if file_exists "$PATH_TO_TRIGGER_FILES" -name '*.control' ; then
    ...
  else
    ...
  fi
fi

Note that /bin/false completely ignores its argument(s), it just returns an exit code of false (1) no matter how many args it has...so this is useful if you only need to test for existence but NOT useful if you need to count the number of matching files. If you needed to do that, you could do something like (with GNU find or any other find that supports -printf):

if [ "$(find . -mtime -1 -printf 1 | wc -c)" -gt 10 ] ; then
  : do something here
fi

This prints a single character for each file found (it doesn't matter which character is printed - I'm using "1" here, but it would work the same for "." or "x" or any other char), and then counts them with wc -c. If there are more than 10, then the if test evaluates as true.

For a non-GNU find without -printf that supports the (also non-POSIX, but still quite common) -print0 option, you could use:

if [ "$(find . -mtime -1 -print0 | awk -v RS='\0' 'END{print NR}')" -gt 10 ] ; then
  : do something here
fi

This version uses awk to print the number of NUL-separated records (i.e. the number of matching files) in find's output.

If you are absolutely certain that none of the file or directory names will ever contain a newline (\n) character, then you could just use the POSIX standard -print instead of -print0 (and pipe into wc -l) - but that is never something you can rely on because you can never really be absolutely certain that such a filename will never exist at some point in the future. i.e. at best, it might work now but could fail tomorrow, or next year.

1

The find command is supposed to return several files, hence the warning you see.

If you want check one file, it would be better to use stat

file_btime=$(stat -c%W /var/opt/test/maintenancelock/testLOCK)
time_now=$(date %s)
file_age=$(( time_now - file_btime ))

if [ $file_age -le 86400 ] ; then
   echo File is less then 24h old
fi
5
  • 3
    ctime is not creation time. You probably want mtime Oct 18, 2022 at 22:18
  • 1
    The "Solaris and Linux" part is tricky: is that stat invocation valid on Solaris? Oct 18, 2022 at 22:55
  • 1
    @roaima, mtime is modification time, ctime or more specifically "birth time" is a time when file was created. For lock files, the ctime is more useful (usually). But if you like to do an mtime - replace -c%W with -c%Y.
    – White Owl
    Oct 18, 2022 at 23:23
  • 5
    @WhiteOwl ctime is the change time which refers to the last time some metadata related to the file was changed. See this Oct 19, 2022 at 1:27
  • The find command is never being executed, it doesn't matter what it returns. The [ command doesn't execute its arguments as a command.
    – Barmar
    Oct 19, 2022 at 15:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .