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 Answers3

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
glenn jackman
  • 85,964
  • 1
    You can devise valid expressions for test with exactly 4 arguments, but you'd need -a or -o to combine 2 expressions. – glenn jackman Oct 18 '22 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 '22 at 14:45
  • I don't see grep in my answer. Maybe you need to ask a separate question. – glenn jackman Oct 19 '22 at 15:02
  • Technically, [ find "..." -mtime -1 ]; passes 5 words to [, namely find, "...", -mtime, -1, and ]. – A. R. Oct 19 '22 at 15:04
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

rrauenza
  • 734
White Owl
  • 5,129
  • 3
    ctime is not creation time. You probably want mtime – Chris Davies Oct 18 '22 at 22:18
  • 1
    The "Solaris and Linux" part is tricky: is that stat invocation valid on Solaris? – glenn jackman Oct 18 '22 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 '22 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 – Edgar Magallon Oct 19 '22 at 01: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 '22 at 15:01
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.

cas
  • 78,579