I want to write a shell script that will operate on multiple files, and (among other things) will make them executable.
The problem: the files have different permissions – in particular, some are readable by all users and some are only readable by the owner – and I want to add the execution permission exactly for those who've got the read permission (that is, if all can read the file, all should be able to execute it, but if only the owner can read it, then only the owner should be able to execute it).
I know how to achieve this by writing a small program in any number of programming languages, and using a few different approaches (from a naive, straight-forward condition on the permissions for each agent to a tricky use of masking and shifting of the permission bits), but I'd like to achieve this in a "pure" shell script, using nothing but standard shell commands and Unix utils.
CodePudding user response:
This is probably not the most efficient way to do it, but I really like the find
command for its intuitive API.
The following command finds all files and directories in the current directory (the .) that are readable by the group or by all users:
find . -perm /go=r
And this command finds all files and directories that are readable by the owner but NOT readable by the group or by all users:
find . -perm /u=r ! -perm /go=r
Once you find the files you want to process, you can add an -exec
argument to execute a command on each of the matches.
For example, the following command would set owner's executable bit on all files and directories that are readable by the owner only:
find . -perm /u=r ! -perm /go=r -exec chmod u x {} \;
The end of the line is quite weird but enter it exactly as shown and it should work.
And this command would set the executable bits for "group" and "others" on all files and directories that are readable by group and others:
find . -perm /go=r -exec chmod go x {} \;
All these commands search recursively in the current directory. You can specify an arbitrary other directory by replacing .
with another path.
You can also specify how deep the searching should go with -maxdepth N
.
CodePudding user response:
If using zsh
as your shell, you can take advantage of glob qualifiers to limit matches to files with and without specific permission bits:
# chmod all plain files (.) that are world-readable (R) but not world-executable (^X)
zsh$ chmod o x *(.R^X)
# files that are owner-readable but not owner-executable
zsh$ chmod u x *(.r^x)
# And groups
zsh$ chmod g x *(.A^E)
You can also do things like *(.X^R)
to get files that are world-executable but not world-readable, or other combinations of having a permission (Before the ^
) and not having it (After the ^
). There's also a f
fspec qualifier for even more control over permission bit matching; see the documentation for details.
To get all files in a directory tree instead of just the current directory, use **/*
instead of *
.