How to recursively chmod all directories except files?
Matthew Barrera
How to chmod 755 all directories but not files (recursively)?
Inversely, how to chmod only files (recursively) but no directories?
9 Answers
To recursively give directories read&execute privileges:
find /path/to/base/dir -type d -exec chmod 755 {} +To recursively give files read privileges:
find /path/to/base/dir -type f -exec chmod 644 {} +Or, if there are many objects to process:
chmod 755 $(find /path/to/base/dir -type d)
chmod 644 $(find /path/to/base/dir -type f)Or, to reduce chmod spawning:
find /path/to/base/dir -type d -print0 | xargs -0 chmod 755
find /path/to/base/dir -type f -print0 | xargs -0 chmod 644 8 A common reason for this sort of thing is to set directories to 755 but files to 644. In this case there's a slightly quicker way than nik's find example:
chmod -R u+rwX,go+rX,go-w /pathMeaning:
-R= recursively;u+rwX= Users can read, write and execute;go+rX= group and others can read and execute;go-w= group and others can't write
The important thing to note here is that uppercase X acts differently to lowercase x. In manual we can read:
The execute/search bits if the file is a directory or any of the execute/search bits are set in the original (unmodified) mode.
In other words, chmod u+X on a file won't set the execute bit; and g+X will only set it if it's already set for the user.
7If you want to make sure the files are set to 644 and there are files in the path which have the execute flag, you will have to remove the execute flag first. +X doesn't remove the execute flag from files who already have it.
Example:
chmod -R ugo-x,u+rwX,go+rX,go-w pathUpdate: this appears to fail because the first change (ugo-x) makes the directory unexecutable, so all the files underneath it are not changed.
3To recursively give directories read&execute privileges:
find /path/to/base/dir -type d -exec chmod 755 {} \;To recursively give files read privileges:
find /path/to/base/dir -type f -exec chmod 644 {} \;Better late than never let me upgrade nik's answer on the side of correctness. My solution is slower, but it works with any number of files, with any symbols in filenames, and you can run it with sudo normally (but beware that it might discover different files with sudo).
4I decided to write a little script for this myself.
Recursive chmod script for dirs and/or files — Gist:
chmodr.sh
#!/bin/sh
#
# chmodr.sh
#
# author: Francis Byrne
# date: 2011/02/12
#
# Generic Script for recursively setting permissions for directories and files
# to defined or default permissions using chmod.
#
# Takes a path to recurse through and options for specifying directory and/or
# file permissions.
# Outputs a list of affected directories and files.
#
# If no options are specified, it recursively resets all directory and file
# permissions to the default for most OSs (dirs: 755, files: 644).
# Usage message
usage()
{ echo "Usage: $0 PATH -d DIRPERMS -f FILEPERMS" echo "Arguments:" echo "PATH: path to the root directory you wish to modify permissions for" echo "Options:" echo " -d DIRPERMS, directory permissions" echo " -f FILEPERMS, file permissions" exit 1
}
# Check if user entered arguments
if [ $# -lt 1 ] ; then usage
fi
# Get options
while getopts d:f: opt
do case "$opt" in d) DIRPERMS="$OPTARG";; f) FILEPERMS="$OPTARG";; \?) usage;; esac
done
# Shift option index so that $1 now refers to the first argument
shift $(($OPTIND - 1))
# Default directory and file permissions, if not set on command line
if [ -z "$DIRPERMS" ] && [ -z "$FILEPERMS" ] ; then DIRPERMS=755 FILEPERMS=644
fi
# Set the root path to be the argument entered by the user
ROOT=$1
# Check if the root path is a valid directory
if [ ! -d $ROOT ] ; then echo "$ROOT does not exist or isn't a directory!" ; exit 1
fi
# Recursively set directory/file permissions based on the permission variables
if [ -n "$DIRPERMS" ] ; then find $ROOT -type d -print0 | xargs -0 chmod -v $DIRPERMS
fi
if [ -n "$FILEPERMS" ] ; then find $ROOT -type f -print0 | xargs -0 chmod -v $FILEPERMS
fiIt basically does the recursive chmod but also provides a bit of flexibility for command line options (sets directory and/or file permissions, or exclude both it automatically resets everything to 755-644). It also checks for a few error scenarios.
I also wrote about it on my blog.
1Try this python script; it requires no spawning of processes and does only two syscalls per file. Apart from an implementation in C, it will probably be the fastest way of doing it (I needed it to fix a filesystem of 15 million files which were all set to 777)
#!/usr/bin/python3
import os
for par, dirs, files in os.walk('.'): for d in dirs: os.chmod(par + '/' + d, 0o755) for f in files: os.chmod(par + '/' + f, 0o644)In my case, a try/catch was required around the last chmod, since chmodding some special files failed.
0I post my solution because I don't see an almost-every cases solution using only chmod:
Only chmod : smooth permissions on files and dirs
For my example I created multiple files with different permissions:
> tree -p chmodtests/
chmodtests/
├── [drwxr-xr-x] aa/
│ ├── [drwxr-xr-x] a1/
│ │ ├── [-r--r--r--] read_only
│ │ ├── [-rw-rw-rw-] read_w
│ │ └── [-rwxrwxrwx] read_wx*
│ ├── [drwxr-xr-x] a2/
│ ├── [-r--------] read_only
│ ├── [-rw-------] read_w
│ └── [-rwx------] read_wx*
└── [drwxr-xr-x] bb/
4 directories, 6 filesthen apply this command:
chmod -vR a=r-wx,u=wr,a+X chmodtests/output:
mode of 'chmodtests/' retained as 0755 (rwxr-xr-x)
mode of 'chmodtests/aa' retained as 0755 (rwxr-xr-x)
mode of 'chmodtests/aa/a1' retained as 0755 (rwxr-xr-x)
mode of 'chmodtests/aa/a1/read_only' changed from 0444 (r--r--r--) to 0644 (rw-r--r--)
mode of 'chmodtests/aa/a1/read_w' changed from 0666 (rw-rw-rw-) to 0644 (rw-r--r--)
mode of 'chmodtests/aa/a1/read_wx' changed from 0777 (rwxrwxrwx) to 0644 (rw-r--r--)
mode of 'chmodtests/aa/read_only' changed from 0400 (r--------) to 0644 (rw-r--r--)
mode of 'chmodtests/aa/a2' retained as 0755 (rwxr-xr-x)
mode of 'chmodtests/aa/read_w' changed from 0600 (rw-------) to 0644 (rw-r--r--)
mode of 'chmodtests/aa/read_wx' changed from 0700 (rwx------) to 0644 (rw-r--r--)
mode of 'chmodtests/bb' retained as 0755 (rwxr-xr-x)result: all files are 644; all dirs are 755
> tree -p chmodtests/
chmodtests/
├── [drwxr-xr-x] aa/
│ ├── [drwxr-xr-x] a1/
│ │ ├── [-rw-r--r--] read_only
│ │ ├── [-rw-r--r--] read_w
│ │ └── [-rw-r--r--] read_wx
│ ├── [drwxr-xr-x] a2/
│ ├── [-rw-r--r--] read_only
│ ├── [-rw-r--r--] read_w
│ └── [-rw-r--r--] read_wx
└── [drwxr-xr-x] bb/Explanation part
tl;dr explanation:
this command removes all execution/search on files and directories and then add execution/search only for dirs
chmod -vR : verbose and recursive
a=r-wx:
a: meaning all (user, group and other)=: set permissions to (do not add nor remove)r-wx: read only permissions
u=wr: user can read and write
a+X: add execution/search only for directories (for all types u,g,o)
Other example
Now let's say I only want 600 for files and 700 for dirs:
chmod -vR a=-rwx,u=rw,u+X chmodtests/
Limits
With this method you cannot set r and w differently for file and dirs.
E.g. you cannot have the following
drwxr-xr-x dir/
-r-------- dir/myfilehth
1You can also use tree:
tree -faid /your_directory | xargs -L1 -I{} bash -c 'sudo chmod 755 "$1"' -- '{}'and if you want to also view the folder add an echo
tree -faid /your_directory | xargs -L1 -I{} bash -c 'sudo chmod 755 "$1" && echo$1' -- '{}' 5 You could use the following bash script as an example. Be sure to give it executable permissions (755). Simply use ./autochmod.sh for the current directory, or ./autochmod.sh <dir> to specify a different one.
#!/bin/bash
if [ -e $1 ]; then if [ -d $1 ];then dir=$1 else echo "No such directory: $1" exit fi
else dir="./"
fi
for f in $(ls -l $dir | awk '{print $8}'); do if [ -d $f ];then chmod 755 $f else chmod 644 $f fi
done 2