Skip to content
shellmap

Set permissions recursively on a directory tree

Apply a single permission change to a directory and every file underneath — for locking down a freshly-extracted tarball, undoing world-writable mistakes, or normalizing permissions on a copied directory.

How to set permissions recursively on a directory tree in each shell

Bashunix
chmod -R 755 dir/

`-R` recursive. BUT: `755` (rwxr-xr-x) sets EXECUTE on regular files too — usually wrong for data. Better: `chmod -R u=rwX,go=rX dir/` — the CAPITAL `X` means "execute only if it's a directory or already executable for someone". This sets dirs to 755 and files to 644 in one pass — the idiomatic correct form.

Zshunix
chmod -R 755 dir/
Fishunix
chmod -R 755 dir/
PowerShellwindows
Get-ChildItem -Path dir -Recurse | ForEach-Object { $acl = Get-Acl $_.FullName; $acl.SetAccessRule((New-Object Security.AccessControl.FileSystemAccessRule "Users", "ReadAndExecute", "Allow")); Set-Acl $_.FullName $acl }

On Windows, prefer ACL inheritance over explicit recursion. Set the ACL on the parent with the inheritance flags `ContainerInherit, ObjectInherit` — children pick it up automatically. The above loops every child explicitly; for large trees, set inheritance on the parent and `icacls dir /reset /t` to clear per-child overrides.

cmd.exewindows
icacls dir /grant Users:(OI)(CI)(RX) /t

`/t` recursive (apply to dir + all children). `(OI)` ObjectInherit (files inherit), `(CI)` ContainerInherit (sub-dirs inherit), `(RX)` ReadAndExecute. `/q` adds quiet mode. `/c` continues on errors (skip files you can't access). `/grant:r` REPLACES existing grants (vs `/grant` which adds).

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • **The split-recursion idiom — files and directories want DIFFERENT permissions**: dirs need execute (`x` = "you can traverse INTO me"); files usually don't (`x` = "this is executable code"). `chmod -R 755 dir/` makes every file executable (wrong for `.txt`, `.json`, etc.). The CAPITAL `X` workaround: `chmod -R u=rwX,go=rX dir/` — `X` means "add execute only if it's a dir OR already has execute for SOMEONE". So `image.jpg` (0644) stays 0644; `dir/subdir/` (0755) stays 0755; `script.sh` (already 0755) stays 0755. The split-by-find alternative: `find dir -type d -exec chmod 755 {} +; find dir -type f -exec chmod 644 {} +` (explicit, easier to grep for).
  • **Common recipes**: "lock down to owner only" — `chmod -R go-rwx dir/` removes all group + other access. "make a web-served tree world-readable" — `chmod -R u=rwX,go=rX dir/`. "fix world-writable mistake" — `chmod -R o-w dir/`. "open up for team sharing" — `chmod -R g+rwX dir/ && chmod g+s dir/` (setgid for inherited group). "default Linux home dir" — `chmod 700 ~/` (no group, no other — the default for many distros).
  • **Recursive can be VERY DESTRUCTIVE** — a single wrong number can break SSH, sudo, or system tools. Classic disaster: `sudo chmod -R 777 /` (run by accident in `/`). Recovery: boot from rescue media + restore from backup, or run `restorecon -R /` (SELinux) / `setfiles -F /etc/selinux/...` (rebuild from policy). For NFS-mounted or symlink-heavy trees: `find` with `-xdev` (stay on one filesystem) and `-P` (don't follow symlinks) is safer than `chmod -R`. ALWAYS preview with `find dir -type f -ls | head` before a wide recursive change.
  • **Performance on huge trees**: `chmod -R` is O(files) — millions of inodes = noticeable wall time. `find dir -type f -exec chmod 644 {} +` (the `+` plus form batches arguments into a few execs instead of one per file — orders of magnitude faster than `\;` semicolon). On SSDs the difference is smaller; on slow disks or network filesystems (NFS, S3FS, EFS), batching is essential. For multi-million-file trees, parallelize: `find dir -type f -print0 | xargs -0 -P 8 chmod 644` (8 parallel processes).
  • **Windows ACL inheritance is the right pattern for "recursive"** — instead of touching every file, set the inheritable ACEs on the parent and let children inherit. `icacls dir /inheritance:e /grant Users:(OI)(CI)(RX) /t` enables inheritance + grants Users RX with object+container inherit. Per-file ACLs that contradict the parent are called EXPLICIT (vs INHERITED). To wipe explicit overrides and force inheritance everywhere: `icacls dir /reset /t /c`. For pwsh, manipulate `Acl.AreAccessRulesProtected = $false` to enable inheritance, then call `Set-Acl`.

Related commands

Related tasks