Sample output
0755 drwxr-xr-x deploy:deploy bin
0640 -rw-r----- deploy:www-data config.env
0777 lrwxrwxrwx deploy:deploy current
0600 -rw------- deploy:deploy id_ed25519
0644 -rw-r--r-- deploy:deploy id_ed25519.pub
2775 drwxrwsr-x deploy:www-data releases
0755 -rwxr-xr-x deploy:deploy restart.sh
0666 srw-rw-rw- deploy:deploy shared.sock
One line needs action. shared.sock is a Unix socket (the s in the type column) that every account on the machine can connect to, and chmod 0660 shared.sock closes that. releases shows a 2 in the first column, which is the setgid bit: new files created inside inherit the www-data group, which is usually deliberate on a deploy directory and worth knowing before you “fix” it. The 0777 on current is not a problem. It is a symlink, and Linux ignores a link’s own mode bits.
When you would reach for it
SSH refuses a key with “UNPROTECTED PRIVATE KEY FILE”, a web server returns 403 on one file out of fifty, or a deploy script needs to copy one file’s permissions onto another. Each time, you need the mode as digits rather than the rwx letters, and converting -rw-r----- to 0640 in your head is how people end up running chmod 777 out of frustration. It is also the quickest way to audit a directory before you hand it to another user.
Gotchas
- macOS ships BSD stat, where
-cis an illegal option. The same output comes fromstat -f '%Lp %Sp %Su:%Sg %N' *:%Lpis the low (permission) part of the mode in octal, and theSprefix asks for the string form of a field.brew install coreutilsprovides GNU stat asgstatif you would rather keep one syntax. - Running the macOS form on Linux does not fail the way you expect. GNU
-ftakes no argument, so the format string is treated as a file name and you getcannot read file system information for '%Lp %Sp %N'followed by a block of filesystem statistics for the files that do exist. - Symlinks are reported as themselves, not their targets, which is why
currentshows0777. Add-Lto follow links and see the mode of the file they point at, which is the one that actually controls access. - The octal shows the classic mode bits only. If
ls -lprints a+after the permissions, an ACL is granting access the number does not mention;getfaclon Linux andls -leon macOS show the rest. A missing path prints an error and sets exit status 1, but stat still reports every other file you named.
Variants
$ stat -f '%Lp %Sp %Su:%Sg %N' *
macOS and the BSDs. Same columns, different letters, and no zero-padding of the octal
$ find . -type f ! -perm 644 -exec stat -c '%04a %n' {} +
List every file in a tree whose mode is anything other than 0644. The + packs many names into each stat call instead of starting one process per file
$ echo $(( $(date +%s) - $(stat -c %Y server.log) ))
Seconds since a file was last modified. %Y is the mtime as a Unix timestamp, so the age is plain integer arithmetic with no date parsing