-2

I am struggling to write a find command in the if condition, Let me share my requirement first. i have a list of files in a file named /tmp/fmxfile

cat /tmp/fmxfile
/fmx/eng/hint.fmx
/pll/case.pll
/plx/eng/case.plx

i am writing a for loop above file, (to look for each file in the different path if condition passes) In the first IF condition first i want to check if file is there in that path In the Second IF condition i want to find if that file is modified in last 5 mins , if it is true then cp that file to the /tmp path.

cd /apps/common/bau/base/
for file in `cat /tmp/fmxfileslist`
do
if [ -f $file ]
then
    echo "File '${file}' found in $(pwd) path and created on $(date +%D)."
    if [ $(find $file -mmin -5 -type f) ="true" ];
    then
    cp -a $file /tmp/basefmxfiles
    ls -lrt | grep /tmp/basefmxfiles
    else 
    echo "But the file is not modified in last 5 minutes"
    fi
else
echo "$0: File '${file}' not found."
fi
done
telcoM
  • 96,466
afrin
  • 61
  • 1.) Your described list of files was /tmp/fmxfile, but your script reads filenames from /tmp/fmxfileslist. Was that just an error in writing the question, and if not, what's in /tmp/fmxfileslist? 2.) If your script actually reads /tmp/fmxfile, the $file will refer to those exact absolute paths listed in that file, not to anything under /apps/common/bau/base. So your first if will look into a wrong place and your first echo will tell lies. 3.) date +%D will just show the current date, not the creation date of any file. 4.) find will never output "true" when used like that. – telcoM May 31 '23 at 07:24
  • This question seems like homework exercise. If you get an answer for free, what will happen in a final exam when you'll have to write a different script with no outside help? – telcoM May 31 '23 at 07:29
  • can you please help me to correct the script , I want to search for the files of /tmp/fmxfile in this path /apps/common/bau/base and check if that file is modified in last 5 mins then copy those specific file to /tmp/basefmxfiles – afrin May 31 '23 at 07:36
  • So for the first file, do you want to check a) exactly /apps/common/bau/base/fmx/eng/hint.fmx only, or b) exactly /apps/common/bau/base/hint.fmx only, or c) hint.fmx in /apps/common/bau/base or any sub-directory of it? – telcoM May 31 '23 at 07:42

1 Answers1

2

Some comments over your code (assuming it's meant to be in sh syntax):

cd /apps/common/bau/base/

You're not checking the exit status of cd, so if it fails, you'll carry on running the rest of the script in the current working directory, whatever it is.

for file in `cat /tmp/fmxfileslist`

That uses split+glob on top of the deprecated `...` form of command substitution, while it looks like you want to iterate over the lines of the file for which the syntax would be:

while IFS= read -r file <&3; do
  ...
done 3< /tmp/fmxfileslist

(or should it be /tmp/fmxfile instead)?

Also note that using files with fixed names in world-writable directories such as /tmp is bad practice from a security standpoint.

Also, none of the path in your sample list are relative, so does it matter what the current working directory is? Or should the list be:

fmx/eng/hint.fmx
pll/case.pll
plx/eng/case.plx

instead? Or should the leading / be stripped or a . inserted before it?

do
if [ -f $file ]

That's using split+glob again over the expansion of $file, which doesn't make sense. You likely meant [ -f "$file" ] if the intention was to check that the file exists, is accessible to you and can be determined to be regular (not directory, fifo, device, socket...) after symlink resolution.

then
    echo "File '${file}' found in $(pwd) path and created on $(date +%D)."

$(pwd) this time using the modern form of command substitution can be written $PWD in POSIX shells. date +%D prints the current date. To print the last modification time of $file, assuming GNU date, you can use the -r option. %D gives dates in mm/dd/yy format which is very ambiguous and doesn't give any information of time nor timezone. $(date -r "$file" +%FT%T%z || echo UNKNOWN).

Also note that echo can't be used for arbitrary data. Use printf '%s\n' "..." instead.

    if [ $(find $file -mmin -5 -type f) ="true" ];

Again, using split+glob on both $file and the output of find. You're also missing a space after the = so the [ command won't see a = comparison operator argument but a bogus =true one.

Also, as find will print the file paths that it finds, it will only ever print true if $file is true (true followed by any number of newline characters would also work as trailing newlines are stripped by command substitution, but as you're reading your fmxfileslist file one line at a time, that cannot happen; IOW your fmxfileslist file cannot include arbitrary file names).

Also note that find will choke on file names starting with - (also on !, (...).

And -type f returns false for symlinks to regular files while the [ -f "$file" ] above would return true. You'd need the GNU specific -xtype f to get consistency between the two or use -L to also check the modification time of the target of the symlink (as date -r also does), so, assuming GNU find 4.9 or newer:

if [ "$(
  printf '%s\0' "$file" | find -L -files0-from - -prune -type f -mmin -5 -printf true)" = true ]
then
  printf '%s\n' "$file is found to be regular after symlink resolution and has been last modified within the last 5 minutes or in the future"
fi

Here passing the file path NUL-delimited on find's stdin (which it gets from there with -files0-from -, the part needing 4.9 or newer), instead of as argument to avoid the choking issue mentioned above.¹

    then
    cp -a $file /tmp/basefmxfiles

Again, non-sensical split+glob, and you're also missing the -- option delimiter. If /tmp/basefmxfiles didn't exist as a directory beforehand, that would create it as a copy of $file instead, so it's a good habit to append a / to the end of the target directory to avoid that (or here since you're already using GNUisms, use GNU cp's -t option):

cp -a -- "$file" /tmp/basefmxfiles/
cp -at /tmp/basefmxfiles -- "$file"
    ls -lrt | grep /tmp/basefmxfiles

ls -lrt lists the contents of the current working directory (meant to be /apps/common/bau/base/ here), and the only way to have /tmp/basefmxfiles in that output is if there's some:

lrwxrwxrwx 1 user group 1 May 17 18:11 somelink -> foo/tmp/basefmxfilesbar

symlink in there, so it's unclear what you meant to do with that command.

    else 
    echo "But the file is not modified in last 5 minutes"
    fi
else
echo "$0: File '${file}' not found."

That's the else part of your [ -f $file ], which checks whether $file is a regular file, so the error (which should go to stderr) should rather be:

printf>&2 '%s\n' "$0: File '$file' not found as a regular file."

Here, you could do every thing with find (again assuming GNU find 4.9 or above):

cd /apps/common/bau/base || exit
tr '\n' '\0' < /tmp/fmxfileslist |
  find -L -files0-from -                                    \
    -prune                                                  \
    -type f                                                 \
    -printf '%p regular and last modified on %TFT%TT%Tz\n'  \
    '('                                                     \
       -mmin -5 -printf '%p regular and mtime > -5min\n' -o \
          -printf '%p regular but older\n'                  \
    ')' -o -printf '%p not a regular files\n'

For files that find can't access, it will print an error message on stderr.

Or you could use zsh instead of sh:

#! /bin/zsh -
cd /apps/common/bau/base || exit
      files=( ${(f)&quot;$(&lt;/tmp/fmxfileslist)&quot;} )
   regulars=( $^files(N-.)                  )

recent_regulars=( $^regulars(N-m-5) ) older_regulars=( ${regulars:|recent_regulars} ) non_regulars=( ${files:|regulars} )

 accessible=( $^files(N-^@)                 )

inaccessible=( ${files:|accessible} )

And for instance print the contents of those arrays raw on 1 Column with print -rC1 -- $array or iterate over them with for file ($array) something with $file (there's no split+glob over unquoted parameter expansions in zsh, so it's fine to leave them unquoted there).

To get the last modification time of a file, before or after symlink resolution you can use its stat builtin:

zmodload zsh/stat
stat -F %FT%T%z -H after -- $file &&
  stat -LF %FT%T%z -H before -- $file &&
  print -r "mtime before symlink resolution: $before[mtime]; after: $after[mtime]"

¹ On BSDs, you can use the find -f "$file" ... syntax instead.

  • 1
    master class in shell scripting!! Archemar bow – Archemar May 31 '23 at 08:04
  • Hi Please share the command with echo command below if [ "$( printf '%s\0' "$file" | find -L -files0-from - -prune -type f -mmin -5 -printf true)" = true ] then printf '%s\n' "$file is found to be regular after symlink resolution and has been last modified within the last 5 minutes or in the future" fi – afrin May 31 '23 at 10:11
  • @afrin, not sure what you mean. echo is not a command that should be used to output arbitrary data. See Why is printf better than echo?. With a UNIX compliant echo (like the builtin one in bash after set -o posix; shopt -s xpg_echo), you could do echo "$file\00\c" to print the file's name NUL-delimited, but that wouldn't work properly for file names that contain backslash characters. – Stéphane Chazelas May 31 '23 at 10:39
  • i want to execute this shell script in the Linux machines and it is not working with printf – afrin May 31 '23 at 10:59
  • @afrin, if it's not working, it's unlikely to be about printf which has been standard for decades. More likely your find version is too old or is not the GNU one. Check with find --version. – Stéphane Chazelas May 31 '23 at 11:01