获取路径的多级基名称



我正在尝试编写一个类似于UNIX basename的程序,除了我可以控制其基础的级别。

例如,程序将执行如下任务:

$PROGRAM /PATH/TO/THE/FILE.txt 1
FILE.txt # returns the first level basename
$PROGRAM /PATH/TO/THE/FILE.txt 2
THE/FILE.txt #returns the second level basename
$ PROGRAM /PATH/TO/THE/FILE.txt 3
TO/THE/FILE.txt #returns the third level base name

试图用perl编写这个,为了快速测试我的想法,我使用以下命令行脚本来获取二级basename,但无济于事:

$echo "/PATH/TO/THE/FILE.txt" | perl -ne '$rev=reverse $_; $rev=~s:((.*?/){2}).*:$2:; print scalar reverse $rev'
/THE

如您所见,它只打印出目录名称,而不是其余部分。我觉得这与与量词的非贪婪匹配或其他什么有关,但我在这方面的知识不足。

如果有更有效的方法可以在 bash 中做到这一点,请告知

如果您在替换中使用$1而不是$2,您会发现您自己的解决方案工作正常。捕获按照其左括号在正则表达式中的显示顺序进行编号,并且您希望保留最外层的捕获。但是,代码不够优雅。

File::Spec模块是实现此目的的理想选择。它一直是每个 Perl v5 版本的核心模块,因此不需要安装。

use strict;
use warnings;
use File::Spec;
my @path = File::Spec->splitdir($ARGV[0]);
print File::Spec->catdir(splice @path, -$ARGV[1]), "n";

输出

E:Perlsource>bnamen.pl /PATH/TO/THE/FILE.txt 1
FILE.txt
E:Perlsource>bnamen.pl /PATH/TO/THE/FILE.txt 2
THEFILE.txt
E:Perlsource>bnamen.pl /PATH/TO/THE/FILE.txt 3
TOTHEFILE.txt

一个纯粹的 bash 解决方案(不检查参数的数量等等):

#!/bin/bash
IFS=/ read -a a <<< "$1"
IFS=/ scratch="${a[*]:${#a[@]}-$2}"
echo "$scratch"

做。

工作原理如下:

$ ./program /PATH/TO/THE/FILE.txt 1
FILE.txt
$ ./program /PATH/TO/THE/FILE.txt 2
THE/FILE.txt
$ ./program /PATH/TO/THE/FILE.txt 3
TO/THE/FILE.txt
$ ./program /PATH/TO/THE/FILE.txt 4
PATH/TO/THE/FILE.txt
#!/bin/bash                                                                                                                    
[ $# -ne 2 ] && exit
input=$1
rdepth=$2
delim=/
[ $rdepth -lt 1 ] && echo "depth must be greater than zero" && exit
parts=$(echo -n $input | sed "s,[^$delim],,g" | wc -m)
[ $parts -lt 1 ] && echo "invalid path" && exit
[ $rdepth -gt $parts ] && echo "input has only $parts part(s)" && exit
depth=$((parts-rdepth+2))
echo $input | cut -d "$delim" -f$depth-

用法:

$ ./level.sh /tmp/foo/bar 2
foo/bar

这是一个 bash 脚本来使用它awk

#!/bin/bash
level=$1
awk -v lvl=$level 'BEGIN{FS=OFS="/"}
    {count=NF-lvl+1;
    if (count < 1) {
        count=1;
    }
    while (count <= NF) {
        if (count > NF-lvl+1 ) {
            printf "%s", OFS;
        }
        printf "%s", $(count);
        count+=1;
    }
    printf "n";
}'

要使用它,请执行以下操作:

$ ./script_name num_args input_file

例如,如果文件input包含行" /PATH/TO/THE/FILE.txt

$ ./get_lvl_name 2 < input
THE/FILE.txt
$

正如@tripleee所说,在路径分隔符上拆分("/"表示类Unix),然后粘贴回一起。 例如:

echo "/PATH/TO/THE/FILE.txt" | perl -ne 'BEGIN{$n=shift} @p = split ///; $start=($#p-$n+1<0?0:$#p-$n+1); print join("/",@p[$start..$#p])' 1
FILE.txt
echo "/PATH/TO/THE/FILE.txt" | perl -ne 'BEGIN{$n=shift} @p = split ///; $start=($#p-$n+1<0?0:$#p-$n+1); print join("/",@p[$start..$#p])' 3
TO/THE/FILE.txt

只是为了好玩,如果你提供分隔符作为第二个参数,这里有一个适用于 Unix 和 Windows(以及任何其他)路径类型:

# Unix-like
echo "PATH/TO/THE/FILE.txt" | perl -ne 'BEGIN{$n=shift;$d=shift} @p = split /Q$dE/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 /
TO/THE/FILE.txt
# Wrong delimiter
echo "PATH/TO/THE/FILE.txt" | perl -ne 'BEGIN{$n=shift;$d=shift} @p = split /Q$dE/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 \
PATH/TO/THE/FILE.txt
# Windows
echo "C:UsersNameDocumentsdocument.doc" | perl -ne 'BEGIN{$n=shift;$d=shift} @p = split /Q$dE/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 \
NameDocumentsdocument.doc
# Wrong delimiter
echo "C:UsersNameDocumentsdocument.doc" | perl -ne 'BEGIN{$n=shift;$d=shift} @p = split /Q$dE/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 /
C:UsersNameDocumentsdocument.doc

相关内容

  • 没有找到相关文章

最新更新