比方说,我有一个包含数百或数千个文件的文件夹,所有文件均以以下架构命名:
<random number of variable length>_<date code in YYYYMMDD format>.jpg
例子:
73923_20180927.jpg
4457582_20180927.jpg
...
18733557_20190401.jpg
23573_20190401.jpg
...
我期望 bash 脚本执行的操作是打印这些日期代码的列表,即
20180927
20190401
...
这听起来像是一项更容易的任务,但实际上是一项更容易的任务。由于架构始终相同,因此我已经实现了应用字符串操作,以便仅打印文件名的所需部分。但是,我仍在弄清楚如何仅打印每个日期一次。
有没有一个巧妙的方法来解决这个问题?
答案1
假设文件名全部匹配模式./*_*.jpg
:
for name in ./*_*.jpg; do
name=${name##*_} # 4457582_20180927.jpg --> 20180927.jpg
printf '%s\n' "${name%.jpg}" # 20180927.jpg --> 20180927
done | sort -u
这会迭代所有名称。对于每个名称,它会删除匹配的最长前缀字符串*_
。然后它输出删除后缀的剩余字符串.jpg
。
然后对所有字符串进行排序,以便最后只输出唯一字符串的列表。
如果存在目录可能为空的风险,则应nullglob
在循环 ( shopt -s nullglob
) 之前设置 shell 选项。这将使循环根本不运行,而不是使用 中未扩展的通配模式运行一次$name
。
没有特殊原因,这是如何做到这一点的sort
:
declare -A skip=()
for name in ./*_*.jpg; do
key=${name##*_} # 4457582_20180927.jpg --> 20180927.jpg
key=${key%.jpg} # 20180927.jpg --> 20180927
if [[ ! -v skip[$key] ]]; then
printf '%s\n' "$key"
skip[$key]=1
fi
done
在这里,我跟踪哪些字符串已作为关联数组 中的键输出skip
。如果字符串对应于数组中的某个键,则不会输出该字符串。
答案2
假设确实没有不正确的文件名,则在该目录中运行:
ls -U | awk '-F[_.]' '{ print $2 }' | sort | uniq