3

For example, I have a directory

/path/to/directory

and I want to set its subdirectories' permissions to something. This is easy:

find /path/to/directory -type d -exec chmod something {} +

But how do I do it in reverse? I need to set the same permission to

/path

and to

/path/to

and to

/path/to/directory

I have a lot of directories like this and I'm looking for some scripting solution

1
  • 1
    It's not clear how many paths you have, but GNU find has a -maxdepth option. find /path -type d -maxdepth 3 for example, if they all share some common root point. Commented Apr 17, 2017 at 13:52

3 Answers 3

2

Recurse upwards until / is reached and call a function for each directory.

#!/bin/sh

function mangleperms {
    echo DEBUG would chmod 755 "$1"
}

function walktoroot {
    DIR="$1"
    HANDLE="$2"
    if [ "$DIR" = "/" ]; then
        return
    fi
    "$HANDLE" "$DIR"

    # recursion (noun): see recursion
    PARENTDIR=`dirname "$DIR"`
    walktoroot "$PARENTDIR" "$HANDLE"
}

if [ -z "$1" ]; then
    echo >&2 "Usage: walktoroot dir"
    exit 1
fi
# TODO probably more edge cases on relative dirs, though there are means
# to fully qualify those
if [ "$1" = "." ]; then
    DIR=`pwd`
else
    DIR=$1
fi

walktoroot "$DIR" mangleperms

E.g.

$ pwd
/var/tmp/a/b/c
$ /home/jdoe/walktoroot .
DEBUG would chmod 755 /var/tmp/a/b/c
DEBUG would chmod 755 /var/tmp/a/b
DEBUG would chmod 755 /var/tmp/a
DEBUG would chmod 755 /var/tmp
DEBUG would chmod 755 /var
$ 
2
  • I'm having trouble getting this to work. I've saved it in $HOME and run chmod +x, but when I try running $HOME/walktoroot . I get 3: function: not found, the DEBUG print, and 5: Syntax error: "}" unexpected.
    – Filip S.
    Commented Feb 23, 2022 at 10:10
  • I changed the shebang to #!/bin/bash and now it works correctly!
    – Filip S.
    Commented Feb 23, 2022 at 10:12
1

With find we can do this as follows:

find /path/to/dir -type d -prune -exec chmod 755 {} \; -exec sh -c '
   while { set -- "${1%/*}"; case $1 in "" ) break ;; esac; }
   do find "$1" -type d -prune -exec chmod 755 \{\} \;; done
' {} {} \;

Another method using a recursive function:

   fx() {
      case $1 in ?* ) chmod 755 "$1"; fx "${1%/*}" ;; esac
   }
   # and then...
   fx /path/to/dir
0

Well, a path is just a string. So you can split it on / and apply your command to each result. For example, you can use a little perl one-liner to print out the parent paths:

$ path=/path/to/some/deeply/buried/thing
$ echo "$path" | perl -F"/" -lane 'for(1..$#F){print join("/",@F[0..$_])}'
/path
/path/to
/path/to/some
/path/to/some/deeply
/path/to/some/deeply/buried
/path/to/some/deeply/buried/thing

If you now make a function out of that perl command (run this command or add it to your shell's initialization file—~/.bashrc if you're using bash):

pexpand(){ 
   printf '%s\n' "$1" | perl -F"/" -lane 'for(1..$#F){print join("/",@F[0..$_])}'; 
}

Now, you can use it like this:

$ pexpand $path
/path
/path/to
/path/to/some
/path/to/some/deeply
/path/to/some/deeply/buried
/path/to/some/deeply/buried/thing

And you can run commands on each directory in that path with:

chmod 733 "$(pexpand "$path")"

The approach above will fail if your path can contain newlines. A more robust version is:

pexpand(){ 
 perl -le '@F=split(/\//, $ARGV[0]); 
           for(1..$#F){printf "%s\0",join("/",@F[0..$_])}' "$1"; 
}  

This is more cumbersome to use though:

pexpand "$path" | while IFS= read -r -d '' d; do chmod 733 "$d"; done

So, if you need this to work on arbitrary input, I would instead recommend writing a little script that takes both the path and the command as input:

#!/usr/bin/perl

my $comm = $ARGV[0] || die "At least 2 arguments are necessary\n";
my $path = $ARGV[1] || die "At least 2 arguments are necessary\n";

my @paths=split(/\//, $path);
for (1..$#paths) {
    system("$comm \"" . join("/",@paths[0..$_]) . "\"");
}

Save that script as something (foo.pl or whatever) in a directory in your $PATH, make it executable (chmod a+x ~/bin/foo.pl), and you can now run it like this:

foo.pl  "chmod 700" /path/to/some/deeply/buried/thing

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .