我正在尝试编写一个与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中编写这个,并且为了快速测试我的想法,我使用以下命令行脚本来获取第二级基本名称,但无济于事:
$echo "/PATH/TO/THE/FILE.txt" | perl -ne '$rev=reverse $_; $rev=~s:((.*?/){2}).*:$2:; print scalar reverse $rev'
/THE
如您所见,它只打印出目录名而不是其他名称。 我觉得这与量词与量词的不匹配有什么关系,但我的知识缺乏那个领域。
如果有更有效的方法在bash中执行此操作,请告知
答案 0 :(得分:3)
如果您在替换中使用$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:\Perl\source>bnamen.pl /PATH/TO/THE/FILE.txt 1
FILE.txt
E:\Perl\source>bnamen.pl /PATH/TO/THE/FILE.txt 2
THE\FILE.txt
E:\Perl\source>bnamen.pl /PATH/TO/THE/FILE.txt 3
TO\THE\FILE.txt
答案 1 :(得分:2)
一个纯粹的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
答案 2 :(得分:1)
#!/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
答案 3 :(得分:1)
以下是使用awk
执行此操作的bash脚本:
#!/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
$
答案 4 :(得分:1)
正如@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$d\E/; $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$d\E/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 \\
PATH/TO/THE/FILE.txt
# Windows
echo "C:\Users\Name\Documents\document.doc" | perl -ne 'BEGIN{$n=shift;$d=shift} @p = split /\Q$d\E/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 \\
Name\Documents\document.doc
# Wrong delimiter
echo "C:\Users\Name\Documents\document.doc" | perl -ne 'BEGIN{$n=shift;$d=shift} @p = split /\Q$d\E/; $start=($#p-$n+1<0?0:$#p-$n+1); print join($d,@p[$start..$#p])' 3 /
C:\Users\Name\Documents\document.doc