I'm trying to change the permissions of all the subdirectories by making a simple bash for
loop:
for dir in `find . -type d`; do chmod 755 "$dir"; done
however, it complains about non-existing directories. By simply printing the directory names from the loop (replacing chmod 755 "$dir"
with echo "$dir"
) I've worked out that the problem occurs when a directory has spaces in its name. What happens is that the for
loop splits the results of find
on every newline and space.
I'd like to somehow make it split the results only according to newlines and ignore the spaces. The double quotes should make sure that the string reaches chmod
as one argument. How do I change the splitting?
The usual solution to this problem is:
find ... -print0 | xargs -0 ...
The -print0
argument causes the output filenames to be nul-terminated and the -0
/ -null
argument to xargs tells it to read such a format.
So in your case...
$ find . -type d -a -print0 | xargs -0 chmod 755
You don't actually need the shell loop at all.
Here are some more options for your toolkit. If you just have a single command to execute on groups of files/directories, find
can do it directly:
find . -type d -exec chmod 755 {} +
If the command can only work on one file/directory at a time, use \;
instead of +
to run once for each item:
find . -type d -exec chmod 755 {} \;
And if you need to do something complex (i.e. several commands), you can make a safe version of the loop:
while IFS= read -r -u3 -d $'\0' dir; do
sudo chown gkudelis "$dir"
chmod 755 "$dir"
touch "$dir/.perms_reset"
done 3< <(find /tmp -type f -print0)
Note that this uses a redirect from a process substitution (<(...)
) instead of a pipe to avoid running the loop in a subshell; this is a bash-only feature, so you must start the script with #!/bin/bash
, not #!/bin/sh
. Also, it does the redirect to fd 3 instead of stdin, so if anything inside the loop tries to read from stdin it won't get confused. Also, be sure to use double-quotes around all references to the loop variable ("$dir"
in this example) to protect spaces in it. See BashFAQ #020 for more details.
Try doing this instead (using builtins, requires bash --version
>= 4) :
shopt -s globstar
for dir in **/*/; do
chmod 0755 "${dir%/}"
done
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