Bash 获取数组中基于子字符串的唯一文件名列表
我有一个目录Bash 获取数组中基于子字符串的唯一文件名列表,bash,shell,Bash,Shell,我有一个目录my_dir,其中的文件名如下: a_v5.json a_v5.mapping.json a_v5.settings.json f_v39.json f_v39.mapping.json f_v39.settings.json f_v40.json f_v40.mapping.json f_v40.settings.json c_v1.json c_v1.mapping.json c_v1.settings.json 我正在寻找一种在bash中获取数组[a_v5,f_v40,c_v
my_dir
,其中的文件名如下:
a_v5.json
a_v5.mapping.json
a_v5.settings.json
f_v39.json
f_v39.mapping.json
f_v39.settings.json
f_v40.json
f_v40.mapping.json
f_v40.settings.json
c_v1.json
c_v1.mapping.json
c_v1.settings.json
我正在寻找一种在bash中获取数组
[a_v5,f_v40,c_v1]
的方法。在这里,我需要的是具有最新版本号的文件名数组
尝试了以下操作:
ls*.json | find-类型f-exec basename“{}”\;|切割-d-f1
,但它返回的结果中包含扩展名不为.json的文件。这是一种可能的实现方法:
arr=( $( { for name in $( ls {f,n,m}*.txt ); do echo ${name:0:1} ; done; } | sort | uniq ) )
输出:
问候 数组和bash字符串解析
declare -A tmp=()
for f in $SOURCE_DIR/*.json
do f=${f##*/} # strip path
tmp[${f%%.*}]=1 # strip extraneous data after . in filename
done
declare -a c=( $( printf "%s\n" "${!tmp[@]}" | cut -c 1 | sort -u ) ) # get just the first chars
declare -a lst=( $( for f in "${c[@]}"
do printf "%s\n" "${!tmp[@]}" |
grep "^${f}_" |
sort -n |
tail -1; done ) )
echo "[ ${lst[@]} ]"
[ a_v5 c_v1 f_v40 ]
或者,如果你愿意
declare -a arr=( $(
for f in $SOURCE_DIR/*.json
do d=${f%/*} # get dir path
f=${f##*/} # strip path
g=${f:0:2} # get leading str
( cd $d && printf "%s\n" ${g}*.json |
sort -n | sed -n '$ { s/[.].*//; p; }' )
done | sort -u ) )
echo "[ ${arr[@]} ]"
[ a_v5 c_v1 f_v40 ]
AWK解决方案
这不是一个优雅的解决方案。。。我对awk的了解有限。
你会发现这很实用
我已经更新了它,删除了@socowi建议的过时uniq。
我还包括了@socowi建议的printf版本
ls *.json | cut -d. -f1 | sort -rn | awk -v last="xx" '$1 !~ last{ print $1; last=substr($1,1,3) }'
或
旧的理解 查找具有名称匹配模式的文件 现在选择第二个字段,因为您的结果可能类似于/
find . -type f -iname "*.json" | cut -d. -f2
要获得唯一的标题
find . -type f -iname "*.json" | cut -d. -f2 | sort | uniq
如果文件名不包含空格和特殊符号,如
*
或?
,则可以使用以下命令:
array=($(
find . -type f -iname \*.json |
sed -E 's|(.*/)*(.*_v)([0-9]+)\..*|\2 \3|' |
sort -Vr | sort -uk1,1 | tr -d ' '
))
它既丑陋又不安全。下面的解决方案较长,但可以处理所有文件名,甚至是包含换行符的文件名
maxversions() {
find -type f -iname \*.json -print0 |
gawk 'BEGIN { RS = "\0"; ORS = "\0" }
match($0, /(.*\/)*(.*_v)([0-9]+)\..*/, group) {
prefix = group[2];
version = group[3];
if (version > maxversion[prefix])
maxversion[prefix] = version
}
END {
for (prefix in maxversion)
print prefix maxversion[prefix]
}'
}
mapfile -d '' array < <(maxversions)
maxversions(){
find-type f-iname\*.json-print0|
gawk'BEGIN{RS=“\0”;ORS=“\0”}
匹配($0,/(.\/)*(.*U v)([0-9]+)\.*/,组){
前缀=组[2];
版本=组[3];
如果(版本>最大版本[前缀])
maxversion[前缀]=版本
}
结束{
for(maxversion中的前缀)
打印前缀maxversion[前缀]
}'
}
mapfile-d''数组<@anubhava更新了OP。关于您的命令:ls | find
与刚才的find
相同<代码>查找
不读取标准数据。要仅列出json
文件,请使用find-键入f-iname\*.json
@Socowi根据编号获取唯一的文件名如何?对此有什么建议吗?[a_v5,f_v40,c_v1]
是预期的输出。“具有最新版本号的文件名数组”正是我的意思。为我的错误解释道歉。你可以只写{f,n,m}而不是$(ls{f,n,m}*.txt
*.txt
。后者不仅更短,而且更安全、更快。还有,为什么txt
?OP示例中的所有文件都以json
结尾。我没有投票,因为这并不能回答问题。即使这个解决方案是OP要求的,它也只适用于单字母基名称,如a_v1.json
,但不适用于abc_v1.json
。如果您能修复或删除此答案,我将不胜感激。我不知道这将如何解决我的问题。我的糟糕。忘记指定json文件。这将创建您所需的数组,尽管它将同时具有f_v39
和f_v40
。好的。我只需要具有最大/la的文件名测试其中的版本号。你有什么办法过滤掉它吗?误解。给我一点。好的。用你的代码,我得到语法错误:预期的操作数
。你也得到了吗?这并没有给我预期的输出。相反,它给了我设置映射json…
@Akshaymaldure你的环境可能与我的不同。如果您将获得设置、映射和json,您可能确实需要-f1。即使如此(find.-type f-iname”*.json“| cut-d.-f1 | sort | uniq
),我没有得到预期的输出,因为所有的文件名在考虑版本时总是唯一的。@AkshayMaldhure另一种方法是根本不使用find.ls*.json | cut-d.-f1 | sort | uniqs一些改进:用printf%s\\n*.json
替换ls*.json
,我认为uniq
是不必要的nymore.substr($1,1,3)
只适用于格式为a#v#
的文件名,其中a
正好是一个字母。这样,我就得到了sed:1:“s/|(.*/)*(.*u v)([0-9]+)…”:RE错误:空(sub)expression
。顺便说一句,我的文件名中没有空格。我的不好。这就是我在不先检查的情况下进行一些更改所得到的结果:(.您能再试一次吗?这很有效!(不过需要做一些小的修改,即删除数组赋值右侧的前括号和后括号).最后一件事:当文件名中有多个下划线时,您的解决方案应该有效吗?我想是的。但请不要在生产中使用它。awk
命令应该很快完成,并且在任何情况下都应该有效。@akshaymaldure多个下划线没有问题。两种解决方案都应该正确处理它们。如果您有多个\u v12。
然后将使用文件名中第一个点之前的版本。例如:在some/path\u v1.dir/file\u v2.\u v3.json
中,将使用版本v2
。v1
和v3
被忽略。
array=($(
find . -type f -iname \*.json |
sed -E 's|(.*/)*(.*_v)([0-9]+)\..*|\2 \3|' |
sort -Vr | sort -uk1,1 | tr -d ' '
))
maxversions() {
find -type f -iname \*.json -print0 |
gawk 'BEGIN { RS = "\0"; ORS = "\0" }
match($0, /(.*\/)*(.*_v)([0-9]+)\..*/, group) {
prefix = group[2];
version = group[3];
if (version > maxversion[prefix])
maxversion[prefix] = version
}
END {
for (prefix in maxversion)
print prefix maxversion[prefix]
}'
}
mapfile -d '' array < <(maxversions)