Is there a generalized bash function available that mimics chmod
in every aspect, except that it also let's me differentiate between files and directories?
I know there's already numerous examples available, like the following, from this answer:
find /path/to/base/dir -type d -exec chmod 755 {} +
chmod 755 $(find /path/to/base/dir -type d)
find /path/to/base/dir -type d -print0 | xargs -0 chmod 755
…but those are all with hard-coded arguments, and tedious to remember and type out.1)
I'd like to generalize it and make it dynamic, so that I will be able to do something like the following, for instance:
# for directories
chmod -d <any valid argument list for chmod> # or
chmodd <any valid argument list for chmod>
# for files
chmod -f <any valid argument list for chmod> # or
chmodf <any valid argument list for chmod>
I've tried to create a somewhat viable solution myself, but as my bash skills are sub-par and I'm not sure how to parse the correct arguments and insert them into the correct places, it's very crude and limited:
function chmodf {
find . -mindepth 1 -type f -print0 | xargs -0 chmod "$@"
}
function chmodd {
find . -mindepth 1 -type d -print0 | xargs -0 chmod "$@"
}
What I'd preferably like, of course, is something like the following (pseudo code):
function chmodd {
paths = extract paths from arguments list
recursive = extract recursive option from arguments list
otherargs = remaining arguments
if( recursive ) {
find <paths> -mindepth 1 -type d -print0 | xargs -0 chmod <otherargs>
}
else {
find <paths> -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 chmod <otherargs>
}
}
Are you aware of the existence of such a function/binary already, or can you help me on my way to achieving this goal?
The main reason I want this, is that I find myself regularly needing to recursively set the setgid bit on directories, but not on files. But, as far as I am aware there's no capital letter g+S
option for chmod.
1) I'm aware of the unix adagio "Do one thing and do it well", or something to that extent, but to be honest, I can't fathom how, at least to my knowledge, after nearly half a century of the existence of chmod
and seeing numerous requests for this behavior, chmod
has not already been amended with this functionality. It seems like such a obvious and appropriate functionality for chmod
to have.
Best Answer
My attempt:
The function and aliases turn
into
Similarly
chmodd …
turns intofind … -type d …
.It's mainly about rearranging arguments. There is almost no parsing. Everything before
--
is treated asargs for chmod
. Everything after--
is treated asstarting points
forfind
. We don't want to actually runchmod
recursively, but we may or may not runfind
recursively. Non-recursive operation is performed thanks to-prune
in the right place. If there is-R
or--recursive
inargs for chmod
then it will disappear but-prune
will not appear.Notes:
I think the code is portable. I deliberately avoided using arrays.
chmodf
andchmodd
could be functions. Even then they should callchmodx
to keep the code DRY. Implementing them as two independent functions is inelegant.--
is mandatory because I decided to KISS. It's certainly possible to tell apart pathnames from other arguments without separating them with--
. Actuallychmod
does this. It knows its own syntax, all its possible options and such; so everything else must be files to operate on. Embedding this knowledge and functionality in a shell script is WET. If you really want things to work without--
then you should rather solve your problem by improvingchmod
, modifying its source.There are ways to abuse:
starting points
, so you can inject e.g.-maxdepth 3
this way (as3 -maxdepth
becausestarting points
becomepoints starting
).args for chmod
. Ifargs for chmod
include;
or{} +
then the-exec
primary will be terminated early in the line. This allows you to inject more primaries; or to break the whole command by generating invalid syntax.While technically possible, such tricks are ugly. If I needed to complicate things, I would definitely prefer to write a
find
command anew.