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