如何在Bash脚本中向上跳转到父目录结构?

5

有没有更好的方法从脚本所在位置向上多级目录跳转。

这是我目前的代码。

# 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

我希望找到一种简洁的方法来查找第n级别。除了使用for循环,还有其他想法吗?

8个回答

5
dir="/path/to/somewhere/interesting"
saveIFS=$IFS
IFS='/'
parts=($dir)
IFS=$saveIFS
level=${parts[3]}
echo "$level"    # output: somewhere

2
#!/bin/sh
ancestor() {
  local n=${1:-1}
  (for ((; n != 0; n--)); do cd $(dirname ${PWD}); done; pwd)
}

使用方法:

 $ pwd
 /home/nix/a/b/c/d/e/f/g

 $ ancestor 3
 /home/nix/a/b/c/d

2
一个不需要循环的解决方案是使用递归。我想通过向上遍历我的当前工作目录来查找脚本的配置文件。 要检查当前目录是否在GIT存储库中:rtrav .git $PWD 将检查第一个参数给出的文件名在第二个参数给出的每个父文件夹中的存在情况。打印找到文件的目录路径或退出错误代码,如果未找到该文件。
谓词(test-e $2 / $1 )可以更换为检查指示遍历深度的计数器。

很好。已添加到我的.bashrc文件中。 - Matt Kneiser

1
如果你可以包含Perl命令的話,這個問題就沒問題了:
$ pwd
/u1/myuser/dir3/dir4/dir5/dir6/dir7

第一条命令列出包含前N个目录(在我的情况下为5个)的目录

$ 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

第二个命令列出了目录 N 级向上(在我的情况下是 5)个目录(我想你想要后者)。

$ 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

当然,要在你的bash脚本中使用它:

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";')

感谢您的回答。我不熟悉perl语言。如果有使用awk和sed的替代方案,将不胜感激。 - froogz3301
@Michael - 不,你不能在awk或sed中这样做 - 逻辑被封装在Perl的File::Spec库和数组切片语法(@dirs2=@dirs[0..$#dir-5])中。但是,你不需要了解perl就可以使用这个答案 - 只需将最后一行原样复制/粘贴到你的shell脚本中(当然要测试),并将“5”更改为你想要的深度即可。 - DVK

1
除了使用for循环,还有其他的想法吗?
在shell中,你无法避免循环,因为传统上它们不支持正则表达式,而是支持glob匹配。而glob模式不支持任何类型的重复计数器。
顺便说一下,在shell中最简单的方法是:`echo $(cd $PWD/../.. && echo $PWD)`,其中`/../..`使其剥离两个级别。
用一点点Perl就可以实现:
perl -e '$ENV{PWD} =~ m@^(.*)(/[^/]+){2}$@ && print $1,"\n"'

在Perl的正则表达式中,{2}表示需要剥离的目录条目数。或者使其可配置化:
N=2
perl -e '$ENV{PWD} =~ m@^(.*)(/[^/]+){'$N'}$@ && print $1,"\n"'

也可以使用Perl的split(), join()splice()来实现,例如:

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

-2 表示从路径中删除最后两个条目。


0

此方法使用实际的 Perl 脚本完整路径 ... TIMTOWTDI 您可以简单地将 $RunDir 替换为要开始的路径 ...

        #resolve the run dir where this scripts is placed
        $0 =~ m/^(.*)(\\|\/)(.*)\.([a-z]*)/; 
        $RunDir = $1 ; 
        #change the \'s to /'s if we are on Windows
        $RunDir =~s/\\/\//gi ; 
        my @DirParts = split ('/' , $RunDir) ; 
        for (my $count=0; $count < 4; $count++) {   pop @DirParts ;     }

        $confHolder->{'ProductBaseDir'} = $ProductBaseDir ; 

0

脚本目录的上两级:

echo "$(readlink -f -- "$(dirname -- "$0")/../..")"

所有的引用和--都是为了避免在处理棘手路径时出现问题。


0

这使您可以逐步提高,直到达到所需的任何条件

WORKDIR=$PWD
until test -d "$WORKDIR/infra/codedeploy"; do
  # get the full path of the script

  WORKDIR=$(dirname $WORKDIR)
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接