There has to be a way to stream line those 3 find lines in my crudely constructed script below...
#!/usr/bin/bash
#
#
if [ "find /data -type f -perm 400 -print" ] ; then
find /data -type f -perm 400 -exec echo Modifying {} \;
find /data -type f -perm 400 -print | xargs chmod 755
else
echo 'No files to modify'
fi
You can change it to the following:
found=$(find /data -type f -perm 400 -print -quit)
if [ "$found" ]; then
find /data -type f -perm 400 -exec echo Modifying {} \; -exec chmod 755 {} \;
else
echo 'No files to modify`
fi
Explanation:
find /data -type f -perm 400 -print -quit
: As soon as this finds the very first file with permission 400
in /data
, it prints the file name and terminates the find immediately. The print is saved to the variable found
(So we know whether to output "No files to modify" or not later).-exec echo Modifying {} \; -exec chmod 755 {} \;
: You can execute multiple commands on each result returned by find through this chaining syntaxfind
returns 0
when nothing is found, so using $?
doesn't work here.while read -d $'\0' file; do
found=yes
echo "Modifying $file"
chmod 755 "$file"
done < <(find /data -type f -perm 400 -print0)
if [ "x$found" != "xyes" ]; then
echo "No files found to modify"
fi
This uses the process substitution feature in Bash. It's equivalent to
find ... | while read ...
except in that case, the while read
is performed in a subshell, so we can't set variables that will be seen in the outer shell. Instead, we can use process substitution. <(cmd)
runs cmd
in a subshell, with its standard output redirected into a named pipe. The name of that pipe is substituted into the outer command.
We then use <
to redirect from this pipe into standard input of the while read
command, which reads each delimited record in turn, and stores the value in the name specified (file
in this case). By default, read
breaks on newlines. For most files, that would be fine; you could do:
while read file; do
...
done < <(find /data -type f -perm 400)
and it would work fine. But it is technically possible to have a newline in a filename, in which case that would break. The -print0
argument to find, and -d $'\0'
argument to read
, causes them each to use a null character as a delimiter, which is not valid in a filename.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With