leponceau.org

Programming And Stuff, You Know The Thing…

BASH: Find Files Having Specific Permissions

Posted at — Apr 8, 2021

The zfs diff command allows to list changed files between zfs filesystem snapshots. This is particularly useful to check whether old snapshots can be safely removed. However, that command does not differentiate between changes in contents and changes in file attributes. For that matter it can be beneficial to avoid commands like chmod -R or chown -R that modify each file’s attributes no matter if the update is actually changing anything. And, yes, there is a way to do that: it’s hidden deep within the manual page for find.

# create a set of files with filename "$perm.txt"
$ for u in $(seq 0 7); do
  for g in $(seq 0 7); do
    for o in $(seq 0 7); do
      touch $u$g$o.txt
      chmod $u$g$o $u$g$o.txt
    done
  done
done

# this gives 8 * 8 * 8 files:
$ ls * | wc -l
512

# get file(s) by exact permission:
$ find * -type f -perm u=rwx,g=w,o=x -exec ls -l '{}' \; | cut -b 1-10
-rwx-w---x
$ find * -type f -perm 721 -exec ls -l '{}' \; | cut -b 1-10
-rwx-w---x

# match any file that is a partial match in the given permissions:
$ find * -type f -perm /u=rwx -exec ls -l '{}' \; | cut -b 1-4 | sort -u
-r--
-rw-
-rwx
-r-x
--w-
--wx
---x
$ find * -type f -perm /u=rwx -exec ls -l '{}' \; | wc -l
448

# get the files whose owner has no permission at all:
$ find * -type f -not -perm /u=rwx -exec ls -l '{}' \; | cut -b 1-4 | sort -u
----
$ find * -type f -not -perm /u=rwx -exec ls -l '{}' \; | wc -l
64

# match any file that matches all given permissions:
$ find * -type f -perm -u=rx -exec ls -l '{}' \; | cut -b 1-4 | sort -u
-rwx
-r-x
$ find * -type f -perm -u=rx -exec ls -l '{}' \; | wc -l
128

# and the same with negative requirements:
$ find * -type f -perm -u=rx -not -perm -u=w -exec ls -l '{}' \; | cut -b 1-4 | sort -u
-r-x
$ find * -type f -perm -u=rx -not -perm -u=w -exec ls -l '{}' \; | wc -l
64

# find files not owned by "root.root":
$ find . -type f -not -user root -or -not -group root

This leads to the following, least-intrusive chmod/chown commands:

find . -not -user $user -or -not -group $group -exec chown $user.$group '{}' \;
find . -type d -not -perm 750 -exec chmod 750 '{}' \;
find . -type f -not -perm 640 -exec chmod 640 '{}' \;

Or as a convenient BASH function:

updatePerms() {
  local dir=$1
  local user=$2
  local group=$3
  local dperms=$4
  local fperms=$5
  find "$dir" -not -user $user -or -not -group $group -exec chown $user.$group '{}' \;
  find "$dir" -type d -not -perm $dperms -exec chmod $dperms '{}' \;
  find "$dir" -type f -not -perm $fperms -exec chmod $fperms '{}' \;
}

updatePerms /my/music/folder root audio 750 640