views:

47

answers:

3

Hello,

Is there a neater way of climbing up multiple directory levels from the location of a script.

This is what I currently have.

# get the full path of the script
D=$(cd ${0%/*} && echo $PWD/${0##*/})

D=$(dirname $D)
D=$(dirname $D)
D=$(dirname $D)

# second level parent directory of script
echo $D

I would like a neat way of finding the nth level. Any ideas other than putting in a for loop?

+1  A: 

If you're OK with including a Perl command:

$ pwd
/u1/myuser/dir3/dir4/dir5/dir6/dir7

The first command lists the directory containing first N (in my case 5) directories

$ perl-e 'use File::Spec; \
          my @dirs = File::Spec->splitdir( \
             File::Spec->rel2abs( File::Spec->curdir() ) ); \
          my @dirs2=@dirs[0..5]; print File::Spec->catdir(@dirs2) . "\n";'
/u1/myuser/dir3/dir4/dir5

The second command lists the directory N levels up (in my case 5) directories (I think you wanted the latter).

$ perl -e 'use File::Spec; \
           my @dirs = File::Spec->splitdir( \
              File::Spec->rel2abs( File::Spec->curdir() ) ); \
           my @dirs2=@dirs[0..$#dir-5]; print File::Spec->catdir(@dirs2)."\n";'
/u1/myuser

To use it in your bash script, of course:

D=$(perl -e 'use File::Spec; \
           my @dirs = File::Spec->splitdir( \
              File::Spec->rel2abs( File::Spec->curdir() ) ); \
           my @dirs2=@dirs[0..$#dir-5]; print File::Spec->catdir(@dirs2)."\n";')
DVK
Thank you for your answer. I am not familiar with perl. Any alternatives using awk, sed would be greatly appreciated.
Michael
@Michael - no, you can't do that in awk or sed - the logic is encapsulated in Perl `File::Spec` library and the array slice syntax (`@dirs2=@dirs[0..$#dir-5]`). However, you don't need to **know** perl to use this answer - just copy/paste the last line into your shell script as-is (after testing of course) and change "5" to your desired depth.
DVK
+1  A: 

Any ideas other than putting in a for loop?

In shells, you can't avoid the loop, because traditionally they do not support regexp, but glob matching instead. And glob patterns do not support the any sort of repeat counters.

And BTW, simplest way is to do it in shell is: echo $(cd $PWD/../.. && echo $PWD) where the /../.. makes it strip two levels.

With tiny bit of Perl that would be:

perl -e '$ENV{PWD} =~ m@^(.*)(/[^/]+){2}$@ && print $1,"\n"'

The {2} in the Perl's regular expression is the number of directory entries to strip. Or making it configurable:

N=2
perl -e '$ENV{PWD} =~ m@^(.*)(/[^/]+){'$N'}$@ && print $1,"\n"'

One can also use Perl's split(), join() and splice() for the purpose, e.g.:

perl -e '@a=split("/", $ENV{PWD}); print join("/", splice(@a, 0, -2)),"\n"'

where -2 says that from the path the last two entries has to be removed.

Dummy00001
+2  A: 
dir="/path/to/somewhere/interesting"
saveIFS=$IFS
IFS='/'
parts=($dir)
IFS=$saveIFS
level=${parts[3]}
echo "$level"    # output: somewhere
Dennis Williamson