Issue
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.
UPDATE: There are currently two answers, both of which suggesting interesting techniques (using zsh's glob qualifiers, and using find
) which seem closely related to what I need, but slightly missing the point of the question, as the challenge I'm facing is not finding which files to work on: the user of the script will tell it which files to work on. Rather, the challenge is, given a specific file, to decide, for example, whether to call chmod o+x
on it or not, based on whether the o+r
permission is already set.
Solution
The answers provided so far present useful ideas, but, as none of them answers the question as asked, I am hereby posting an answer to my own question.
This answer presents three different solutions, each based on a different approach suggested in the previously posted answers; there are other ways too which are not covered (I'll only mention, without showing the actual code, the trick of getting the permissions as an octal number, and then – using some mechanism of performing arithmetic/bit-wise calculations – anding it with 444
to get the r
bits, shifting right 2 bits to get the corresponding x
bits, oring with the original permissions, and setting the result as the new permissions).
All of the solutions below assume working on the file "$FILE"
.
Solution 1 – if
ing on the pattern of the current permissions as text
This is perhaps the most straightforward approach:
permissions=$(stat -f %SA "$FILE")
if [[ $permissions =~ .r........ ]]; then chmod u+x "$FILE"; fi
if [[ $permissions =~ ....r..... ]]; then chmod g+x "$FILE"; fi
if [[ $permissions =~ .......r.. ]]; then chmod o+x "$FILE"; fi
Solution 2 – using find
, taking advantage of the -perm
and -exec
options
This approach uses find
not to find multiple files, but rather to either find the given file (in case it has a certain permission) or to not find anything (otherwise), and to set the matching desired permission in case the file is found (and do nothing otherwise):
find "$FILE" -perm -u=r -exec chmod u+x {} \;
find "$FILE" -perm -g=r -exec chmod g+x {} \;
find "$FILE" -perm -o=r -exec chmod o+x {} \;
The three cases can easily be combined into a single for
loop:
for w in u g o; find "$FILE" -perm -$w=r -exec chmod $w+x {} \;; done
Solution 3 (zsh
only) – using glob qualifiers
This approach runs each of the relevant chmod
commands on "$FILE"
only in case it already matches the corresponding permission. Note || :
is added to avoid errors in case there's no match, i.e. if the file does not have the corresponding permission (also note zsh
still emits a no matches found
warning in this case):
chmod u+x "$FILE"(r) || :
chmod g+x "$FILE"(A) || :
chmod o+x "$FILE"(R) || :
Answered By - Tom Answer Checked By - Clifford M. (WPSolving Volunteer)