我正在 Linux shell 中寻找一种方法,最好是 bash 来根据文件名的前几个字母查找文件的重复项。
这会有用的地方:
我为 Minecraft 构建模组包。从 1.14.4 开始,如果更高版本包中存在重复的 mod,Forge 不再出错。它只是停止最旧版本的运行。帮助查找这些重复项的脚本将非常有利。
清单示例:
minecolonies-0.13.312-beta-universal.jar
minecolonies-0.13.386-alpha-universal.jar
通过快速识别受骗者,我可以保持较小的客户群。
根据要求提供更多信息
没有特定的格式。然而,正如您所看到的,至少有两种流行的格式。此外,社区中没有关于使用或不使用哪种字符的标准。有些使用空格(更恶心),有些使用 [](也很恶心),有些使用 _(更恶心),有些使用 -(首选,但你能做什么)。
https://gist.github.com/be3cc9a77150194476b2000cb8ee16e5示例 mods 文件名列表。已经清理干净,所以里面没有骗人的东西。
https://gist.github.com/b0ac1e03145e893e880da45cf08ebd7a包含一个我故意复制的样本。这是时有发生的过分夸大的事情。
更深入的解释
我意识到这可能会占用大量资源。
我想任意指定要采样的所有文件名的从开始到结束的切片范围。根据该切片查找重复项,然后突出显示重复项。我不需要脚本来实际删除它们。
额外学分
该脚本将为怀疑符合复制标准的文件显示一个菜单,以便轻松删除或重命名。
答案1
过滤可能的重复项
您可以使用一些脚本来过滤这些文件以查找可能的重复项。您可以将所有与至少另一个文件匹配的文件(不区分大小写)移动到新目录中,其名称中第一个破折号、下划线或空格之前的部分。cd
进入你的 jars 目录来运行它。
#!/bin/bash
mkdir -p possible_dups
awk -F'[-_ ]' '
NR==FNR {seen[tolower($1)]++; next}
seen[tolower($1)] > 1
' <(printf "%s\n" *.jar) <(printf "%s\n" *.jar) |\
xargs -r -d'\n' mv -t possible_dups/ --
注意:-r
是一个 GNU 扩展,以避免mv
在找不到可能的重复项时在没有文件参数的情况下运行一次。此外,GNU 参数-d'\n'
通过换行符分隔文件名,这意味着在上面的命令中处理空格和其他常用字符,但不处理换行符。
您可以编辑字段分隔符分配,-F'[-_ ]'
以添加或删除字符来定义我们测试重复的部分的结尾。现在它的意思是“破折号、取消划线或空格”。一般来说,捕获比真实重复情况更多的数据是件好事,就像我在这里所做的那样。
现在您可以检查这些文件。如果您觉得文件数量不是很大,您也可以直接对所有文件执行下一步,而不进行过滤。
目视检查可能的重复项
我建议您使用可视外壳来完成此任务,mc
例如午夜指挥官。您可以mc
使用 Linux 发行版的包管理工具轻松安装。
您可以调用mc
包含这些文件的目录,也可以导航到那里。使用 X 终端,您还可以支持鼠标,但任何操作都有方便的快捷方式。
例如,按照菜单操作Left -> Sorting... -> untick "case sensitive"
将为您提供所需的排序视图。
使用箭头浏览文件,您可以选择其中的许多文件Insert,然后可以复制 ( F5)、移动 ( F6) 或删除 ( F8) 突出显示的选择。以下是过滤后的测试数据的屏幕截图:
答案2
我们有一个解决方案 我已经接受了这个答案,它可以轻松地实现我的 bash 驱动程序的目标,而不涉及像 MC 或 Ranger 这样的 shell 管理器。
#!/bin/bash
declare -a names
xIFS="${IFS}"
IFS="^M"
while true; do
awk -F'[-_ ]' '
NR==FNR {seen[tolower($1)]++; next}
seen[tolower($1)] > 1
' <(printf "%s\n" *.jar) <(printf "%s\n" *.jar) > tmp.dat
IDX=0
names=()
readarray names < tmp.dat
size=${#names[@]}
clear
printf '\nPossible Dupes\n'
for (( i=0; i<${size}; i++)); do
printf '%s\t%s' ${i} ${names[i]}
done
printf '\nWhich dupe would you like to delete?\nEnter # to delete or q to quit\n'
read n
if [ $n == 'q' ]; then
exit
fi
if [ $n -lt 0 ] || [ $n -gt $size ]; then
read -p "Invalid Option: present [ENTER] to try again" dummyvar
continue
fi
#clean the carriage return \n from the name
IFS='^M'
read -ra TARGET <<< "${names[$n]}"
unset IFS
# now remove the filename sans any carriage returns
# from the filesystem
# 12/18/2020
rm "${TARGET[*]}"
echo "removed ${TARGET[0]}" >> rm.log
done
IFS="${xIFS}"
这对我来说效果很好,因为它不需要尝试读取数百个文件名以查找重复项,并且会循环直到我对结果感到满意为止。它还将我的操作保存到日志文件中。
一般来说,我遇到的重复模组很少,但一旦出现,那就很麻烦了。这个脚本极大地改善了我的这种情况。
如果您能让脚本更加智能或用户友好,我很想看看。
编辑:2020 年 11 月 5 日
- 改写了我的想法
- 使用这个脚本好几天了,非常有用
- 它允许我做的是升级我的客户端包,然后将所有减去客户端模组的内容上传到服务器,然后使用此脚本快速清理服务器模组/文件夹。所以现在我的包维护速度更快了!
- 更新了脚本以使用 IFS 并清理菜单中的输出
已编辑:2020 年 12 月 18 日
- 一项微小的更改可以使脚本在更多情况下正常运行。