How to run chmod recursively

Last Updated 2022-11-19

As a quick reference, here’s why [.inline-code]chmod[.inline-code] recursion is tricky:

 # NOT what you want, because files and directories require separate treatment
 chmod -R 644 *
 # The correct way:
 # Set _files_ in the current directory and its subdirectories to 644
 find . -type f -exec chmod 644 "{}" \;
 # Set _directories_ in the current directory to 755
 find . -type d -exec chmod 755 "{}" \;

The short (and wrong) answer to "how do I run chmod recursively" is "with the [.inline-code]-R[.inline-code] switch." For example [.inline-code]chmod -R 644 *[.inline-code] will indeed change everything to [.inline-code]-rw-r--r--[.inline-code] permissions, but that's almost _never_ what you actually want to do. Even at the most basic level, directories and files need different permissions (755 and 644, respectively), and setting both to the same permissions will result in problems.

[#using-find-with-exec-flag]Using [.inline-code]find[.inline-code] with [.inline-code]-exec[.inline-code][#using-find-with-exec-flag]

The correct answer to recursive [.inline-code]chmod[.inline-code] is to use [.inline-code]find[.inline-code] with the [.inline-code]-exec[.inline-code] flag. This will let you target files and directories by type, name, extension, etc., and apply permissions selectively to matching files and directories.

With [.inline-code]find[.inline-code] you can filter out the specific files/types you want to affect, and then use the [.inline-code]-exec[.inline-code] flag to perform a command on the matched files, in this case [.inline-code]chmod[.inline-code]. The [.inline-code]find[.inline-code] command is recursive by default, including all subdirectories (and their subdirectories). You can limit how deep the command traverses with the [.inline-code]-depth n[.inline-code] flag.

To affect only the _files_ in the current directory and its subdirectories, we'll use [.inline-code]find -type f[.inline-code] ("find type file") and apply standard file permissions of 644 to the files:

 $ find . -type f -exec chmod 644 "{}" \;

The [.inline-code]{}[.inline-code] is replaced with each matching file, applying the exec command one at a time, and the quotes allow for spaces and other reserved characters in the filename. [.inline-code]-exec[.inline-code] commands must end with [.inline-code]\;[.inline-code].

To do the same thing with directories, but apply 755 permissions:

 $ find . -type d -exec chmod 755 "{}" \;

With [.inline-code]find[.inline-code], you can also target files by name, creation date, owner, and a host of other options, allowing you to target operations like [.inline-code]chmod[.inline-code] very precisely.

[#a-lot-of-files]When you have a lot of files...[#a-lot-of-files]

To speed up the process when working on a large number of files or directories, you can use [.inline-code]-print0[.inline-code] to send a null-separated list, piping it to [.inline-code]xargs -0[.inline-code] to execute a command on each entry.

 $ find . -type d -print0 | xargs -0 chmod 755
 $ find . -type f -print0 | xargs -0 chmod 644

Note that with the [.inline-code]find[.inline-code] commands listed above, we're using [.inline-code].[.inline-code] (current directory) as our base for finding files. You can insert any path in place of the [.inline-code].[.inline-code] to execute the find from that base, e.g. [.inline-code]/var/www[.inline-code] or whatever you need.

So, to summarize, don't use [.inline-code]chmod -R[.inline-code] in almost any circumstance. Rather, use [.inline-code]find ... -exec chmod ...[.inline-code] to apply permissions selectively to files and directories based on their type or other parameters.