For exemple we have folders named with permission numbers 555 or 700 or 777... how can we take the file name and change all the subfolders and files with permissions based on the folder name ? so if we apply it on the folder 555 the current folder and everything inside will have permission as 555 i've tried
find . -name "[0-9][0-9][0-9]" -type d | xargs chmod -Rv [0-9][0-9][0-9]
but it only takes the first folder name and apply it on everything it i want it to treat each folder name ( named withe permission) seperately thank you.
CodePudding user response:
Not sure understand the question.
Need sample input and sample output to understand.
Suggesting following solution:
dir_name="555"; chmod -R "$dir_name" $(find . -path "*$dir_name*")
Explanation
dir_name="555";
: Assign a variable dir_name
that holds the directory name 555
and the chmod
permission value 555
$(find . -path "*$dir_name*")
: List all files under current directory having 555
somewhere in their path.
chmod -R "$dir_name"
: Chmod command using the value of dir_name
555
CodePudding user response:
I'd use something like this.
find . -type d -name '???' -printf '%f\n' | xargs -I'{}' chmod -R '{}' '{}'
-type d
-- find only directories.-name ???
-- this will cover directories named 555, 700, 777, etcprintf '%f\n'
-- print only directory's name.
To be really safe, I would add another option (-maxdepth
):
find . -maxdepth 1 -type d -name '???' -printf '%f\n' | xargs -I'{}' chmod -R '{}' '{}'
maxdepth 1
-- Don't do any search on subfolders.
CodePudding user response:
i've tried find . -name "[0-9][0-9][0-9]" -type d | xargs chmod -Rv [0-9][0-9][0-9] but it only takes the first folder name and apply it on everything it i want it to treat each folder name ( named withe permission) seperately
Your attempt is wrong in several ways, among them:
The
[0-9][0-9][0-9]
pattern matches names that include characters that are not octal digits.The
[0-9][0-9][0-9]
pattern will be expanded by the shell when it parses the command. If the expansion is successful, then the first matching name will give the mode to apply. If the expansion is not successful then (by default) the pattern itself will be passed through tochmod
, which will reject it as an invalid mode.By default, the
xargs
command will take as many inputs as it can to form arguments to one command. So in your example command, iffind
identifies folders700
,555
,755
, and400
then thexargs
will end up executing something like this:chmod -Rv <... expansion of [0-9][0-9][0-9] ...> 700 555 755 400
Even in the event that the pattern expanded to nothing (which could happen under slightly different circumstances) you have a result similar to before: the first directory name would provide the mode, and all the others would be updated to that mode.
This variation should do what you want:
find . -name "[0-7][0-7][0-7]" -type d |
sed 's,^.*/\([^/]*\)$,\1\n\0,' |
xargs -r -l2 chmod -Rv
The sed
command takes each path emitted by find
, extracts the base name, and puts that on a separate, preceding line. For example, if one of the lines find
emits is
./subdir/755
then sed will change that to
755
./subdir/755
. That is, the mode to apply and the directory to which to apply it.
Then the -l2
option to xargs
tells it to use (at most) two input lines in forming each chmod
command, so you will end up with a series of commands of the form
chmod -Rv 755 ./subdir/755
The -r
option to xargs
is a nicety that tells it to avoid executing any commands at all if no lines are read from the standard input.
Addendum: the sed
expression in more detail
[Note: I have slightly simplified the sed
command given in the original version of this answer.]
The sed
command ...
sed 's,^.*/\([^/]*\)$,\1\n\0,'
... processes each input line according to the given expression, a s
ubstitute command with comma (,
) delimters.
- The target pattern is
^\(.*/\)\([^/]*\)$
. This matches- a sequence starting at the beginning of the line (
^
), - matching any number of any character
.*
, - followed by a slash (
/
) character, - followed by any number of any character other than slash (
[^/]*
) - running to the end of the line (
$
).
- a sequence starting at the beginning of the line (
- The part after the last slash is captured as a group.
- the replacement text is
- the contents of the first (only) capture group (
\1
), followed by - a newline (
\n
), followed by - the whole match (
\0
), which will be the original line because the match is anchored to both the beginning and end of the line.
- the contents of the first (only) capture group (
sed
will match each input line against the target pattern. Any line containing at least one slash will match, and every line emitted by the given find
command will satisfy that criterion. The whole match (which will be the whole line) will be replaced by the given replacement, consisting of two lines -- one containing the tail of the original line following the last slash, and the other containing the whole original line.