我在具有特定命名系统的文件夹中有大量文件。它看起来有点像这样:
my_file_A_a.txt
my_file_A_d.txt
my_file_A_f.txt
my_file_A_t.txt
my_file_B_r.txt
my_file_B_x.txt
my_file_C_f.txt
my_file_D_f.txt
my_file_D_g.txt
my_file_E_r.txt
我想要一个命令行或一系列命令(可以使用临时文件,我有写访问权限),它会返回类似以下内容的内容:
A: 4
B: 2
C: 1
D: 2
E: 1
可以用很多ls -1 *A* | wc -l
命令来完成,但是需要很长时间,因为有几百个“组”需要统计。
此外,每个组的名称都是唯一的。有A
团体,有B
团体,但没有AB
团体。
答案1
假设您的文件名“行为良好”,即它们不包含换行符,则以下ls
和的组合awk
将起作用:
ls -d my_file* | awk -F'_' 'NF==4{count[$3]++} END{for (i in count) printf "%s: %d\n", i, count[i]}'
这将重定向列出启动程序ls
的所有文件的命令的输出。该程序将使用as 字段分隔符并检查第三个字段来跟踪数组中的出现情况,该数组使用组号作为“数组索引”。my_file*
awk
awk
_
count
最后,它会打印每个组发生频率的概述。
注意
- 通过要求恰好 4 个这样的字段,可以对完全畸形的文件名提供“最低”保护。这假设
_
不能是示例中文件名的a
,d
, ,... 部分的一部分。f
- 输出不一定根据类别名称排序。排序顺序取决于循环
awk
中数组索引的遍历方式for (i in count)
。如果需要排序,您可以向 中添加另一个管道sort
。或者,如果您使用 GNU Awk,您可以通过以下方式添加配置设置
规则之前BEGIN{PROCINFO["sorted_in"]="@ind_str_asc"}
NF==4{...}
。这将确保根据数组索引遍历数组,并按字典 (ASCII) 顺序排序。 - 这将适用于开头所述的限制,并且因为您的文件名结构相当简单。一般来说是不鼓励解析输出
ls
。
答案2
for f in my_file_*_*.txt
do
f="${f#my_file_}"
printf "%s\n" "${f%%_*.txt}"
done |
sort |
uniq -c
该for
循环重新格式化每个文件名f
以去掉前导my_file_
和尾随_whatever.txt
,然后对该输出进行排序,并用于uniq
计算每个唯一值的出现次数。
答案3
我会通过通配符循环来处理它,然后使用 bash 的正则表达式功能从文件名中提取字段[[
条件表达式构造。
unset collect
declare -A collect
for f in ./*_*_*_*.txt
do
[[ $f =~ [^_]+_+[^_]+_+([^_]+)_+[^_]+.txt ]] &&
((collect["${BASH_REMATCH[1]}"]++))
done
for group in "${!collect[@]}"
do
printf '%s: %d\n' "$group" "${collect["$group"]}"
done
唯一的括号字段是第三个下划线分隔的字段;一旦捕获,我们就会在关联数组 ( collect
) 中增加该值。
答案4
包含四个下划线分隔字段并以字符串结尾的文件名.txt
与扩展通配模式匹配+([!_])_+([!_])_+([!_])_+([!_]).txt
。每个都+([!_])
匹配一个或多个非下划线字符,就像[^_]+
扩展正则表达式一样。
我们可以通过删除最初的两个字段和最后一个字段以及后缀字符串来从中提取第三个字段.txt
。
#!/bin/bash
shopt -s extglob nullglob
names=( +([!_])_+([!_])_+([!_])_+([!_]).txt )
names=( "${names[@]#+([!_])_+([!_])_}" )
names=( "${names[@]%_+([!_]).txt}" )
printf '%s\n' "${names[@]}" | sort | uniq -c
该脚本仅假设文件名中的第三个字段不包含嵌入的换行符。
在问题中的示例文件名上进行测试:
$ ls
list my_file_A_f.txt my_file_B_x.txt my_file_D_g.txt
my_file_A_a.txt my_file_A_t.txt my_file_C_f.txt my_file_E_r.txt
my_file_A_d.txt my_file_B_r.txt my_file_D_f.txt script
$ ./script
4 A
2 B
1 C
2 D
1 E
您可以通过一个简单的awk
脚本对其进行过滤,将其转换为您想要的任何格式。
$ ./script | awk '{ printf "%s: %d\n", $2, $1 }'
A: 4
B: 2
C: 1
D: 2
E: 1
如果您的名字表现良好,这意味着其中任何一个都没有嵌入换行符,那么您可以稍微简化脚本并使用它cut
。
#!/bin/bash
shopt -s extglob nullglob
printf '%s\n' +([!_])_+([!_])_+([!_])_+([!_]).txt |
cut -d _ -f 3 | sort | uniq -c