How to chmod 755 all directories but no file (recursively) ?

Inversely, how to chmod only files (recursively) but no directory ?

up vote 736 down vote accepted

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
  • 4
    The first two examples fail for directories with too many files: -bash: /bin/chmod: Argument list too long. The last command works with many files, but when using sudo one must be careful to put it before xargs instead of chmod: find /path/to/base/dir -type d -print0 | sudo xargs -0 chmod 755 – Agargara Nov 7 '17 at 1:06
  • 2
    Also to note, these commands are inclusive of the base dir. So in the above example, dir will also be set to 755. – CenterOrbit Jan 16 at 0:16
  • 1
    chmod ... $(find /path/to/base/dir -type ...) fails for filenames with spaces in the name. – Dan Dascalescu Feb 6 at 1:58
  • 5
    I think the most correct (but not fastest) version with respect to spaces and symbols in filenames and number of files is find /path/to/base/dir -type d -exec chmod 755 {} \; (find /path/to/base/dir -type f -exec chmod 644 {} \;). – Peter K Feb 21 at 11:49

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 /path

Meaning:

  • -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.

  • 4
    -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 – släcker Jan 6 '10 at 7:08
  • 21
    This pattern won't fix the situation when someone has done chmod -R 777 since the +X option will not reset existing execute bits on files. Using -x will reset directories, and prevent descending into them. – Andrew Vit Aug 7 '12 at 4:57
  • 2
    @ring0: I am not intending to answer the question literally as posed - nik has already done that perfectly well. I'm pointing out a cheaper solution for the most common case. And yes, you do get different permissions for files and directories with X, as explained in the comments. – bobince Oct 28 '12 at 1:05
  • 5
    go+rX,go-w -> go=rX isn't it ? – Pierre de LESPINAY Sep 22 '14 at 14:24
  • 4
    You can also use chmod u-x,u+X in combination, etc., to remove execute bits for files, but add them for directories. – w0rp Apr 4 '16 at 18:27

If 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 path

Update: this appears to fail because the first change (ugo-x) makes the directory unexecutable, so all the files underneath it are not changed.

  • 1
    This works for me, and I don’t see why it wouldn’t. (Sure, if you did just chmod -R ugo-x path, that might be a problem. But the complete command will do the chmod u+rwX on each directory before it tries to descend into it.) However, I believe that chmod R u=rw,go=r,a+X path is sufficient – and it’s shorter. – Scott Jul 8 '14 at 0:25
  • I found this worked properly; there were no issues with entering directories – Someone Somewhere Jun 16 at 12:54

I decided to write a little script for this myself.

Recursive chmod script for dirs and/or files — Gist

It 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.

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 {} \;

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).

  • This is a downgrade of nik’s answer.  Why do you believe that there is anything wrong with nik’s answer? – Scott Oct 9 at 23:06
  • @Scott, nik's answer fails with (vey) large number of files. – Peter K Oct 25 at 14:54
  • I’m 99% sure that you’re mistaken.  Can you provide any evidence to support your claim? – Scott Oct 25 at 23:56
  • Yes, it looks I'm wrong indeed. find ... + seems to break the line into multiple commands, nice catch! – Peter K Oct 28 at 13:40

Try 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.

You 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' -- '{}'
  • @Scott 1) You are right about +x I changed to 755; 2) 3) to solve this I put the placeholder in single quote like this '{}' – Eduard Florinescu Oct 10 at 5:53
  • @Scott I agree this is not the best answer also is slow but will leave here for "didactic" purposes also the comments will explain further, also people can learn about xargs issues. Single quotes in filenames are themselves a problem for many commands and script that's why I listed all the files containing single quotes and removed them (the quotes I mean) – Eduard Florinescu Oct 10 at 6:16
  • @Scott On my systems I searched for all the files that contained single quotes and replaced the single quotes – Eduard Florinescu Oct 10 at 6:42
  • @Scott How would you fix the fact that xargs doesn't solve correctly the single quotes? – Eduard Florinescu Oct 10 at 7:20

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
    Wow! So many problems! (1) If $1 is not null, but is not the name of a directory (e.g., is a typo), then dir gets set to . with no message. (2) $1 should be "$1" and $dir should be "$dir". (3) You don’t need to say "./"; "." is fine (and, strictly speaking, you don’t need quotes here). (4) This is not a recursive solution. (5) On my system, ls -l … | awk '{ print $8 }' gets the files’ modification times. You need { print $9 } to get the first word of the filename. And even then, (6) this does not handle filenames with white space. … – Scott Jul 7 '14 at 22:59
  • 1
    … And, last but not least (∞) if this script is in the current directory, it will chmod itself to 644, thus making itself non-executable! – Scott Jul 7 '14 at 23:00

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.