The main problem with your script fragment is that you are running find multiple times in a loop (once for each filename in /tmp/fefile
).
This is extremely slow and inefficient because find
is an "expensive" operation (recursing a directory tree with any tool is expensive in both time and disk I/O), not something you should run repeatedly in a loop unless you have no other choice (and there's almost always another, better choice).
It is much better to run find
just once and process its output (e.g. with grep or awk or sed or whatever).
Try something more like this:
find ./dir[1234]/ -type f -mmin -10 -printf '%P\n' | grep -F -f /tmp/fefile
That will output a list of all files in dir1..dir4 that a) were modified in the last 10 minutes and b) match the fixed-string patterns in /tmp/fefile
.
BTW, notice that this does not need a /tmp/base
temporary file (also BTW, hard-coding tempfile names into a script is generally a bad idea, use mktemp
or similar instead. I'd guess your /tmp/fefile
should almost certainly not be hard-coded either, but I don't know what the rest of your script does or how this script fragment is executed)
You may need to tweak the find
and/or grep
options a bit to get exactly what you want - it took me a few minutes examining your script fragment to figure out what it is you're trying to do, and I'm still not 100% sure. I do know that you're using ~20 lines of shell code to very inefficiently do something that you could do much better and faster with either find alone or with find and grep (or some other common tool such as sed or awk or perl).
Note: this will not work correctly if any filenames contain newline characters. You can use \0
instead of \n
in the -printf
format string, along with GNU grep's -z
option.
find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' | grep -z -F -f /tmp/fefile
(to view the output in a terminal, you might want to convert the NUL separators to newlines, e.g. by piping the output to tr '\0' '\n'
. This is fine for just displaying a list of filenames, but not safe if you need to do something with the filenames)
And, speaking of doing things with the filenames, one of the best and safest ways to do that is to store them in an array. e.g. by using the bash built-in mapfile
(AKA readarray
) along with process substitution to populate an array with all the matching filenames.
declare -a found
mapfile -d '' -t found < <(find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' |
grep -z -F -f /tmp/fefile)
$found
will be an array containing all the matching filenames. you can view the array with declare -p found
(this is mostly useful for debugging purposes, to verify that the array contains what you think it should contain) or use it as args for a command, or in a loop, e.g.:
for f in "${found[@]}"; do
echo "$f"
done
You can do anything else you want to do with "$f"
in the loop, but remember to double-quote both the variable AND the array because they can contain any character except NUL.
Which reminds me, you use ${file}
in your find
command, rather than "$file"
. This is a very commonly-made mistake: curly braces around variables are NOT a substitute for quoting.
They are used for parameter substitution (run man bash
and search for the Parameter Expansion
heading) and to eliminate ambiguity when interpolating variable names in a string (e.g. when you have a variable called $foo
and you need to print it in a string immediately adjacent to a valid variable-name character - echo "$food"
will output the value of $food, while echo "${foo}d"
will output the value of $foo followed by a literal d
character).
See $VAR vs ${VAR} and to quote or not to quote.
See also Why does my shell script choke on whitespace or other special characters?, When is double-quoting necessary? and Security implications of forgetting to quote a variable in bash/POSIX shells
Finally, since this question is about find
and processing its output, and because you have been asking several find-related questions, see Why is looping over find's output bad practice?. And don't forget to read the related questions it links to.
-name ${file}
suggests though that they may want foramx/eng/prf.amx
for instance to find a file called prf.amx in the eng subdir of an amx directory, and not any file whose path containamx/eng/prf.amx
(likekabamx/eng/prf.amxil
), in which case it may be necessary to switch fromgrep -zF
toperl -0
and construct the appropriate regex. – Stéphane Chazelas Jun 05 '23 at 06:44