所以我有一个文件列表,我将其重命名为filename:hash
.
我想做的是仅有的匹配哈希,同时保持组合filename:hash
完整,并且不会再次计算其哈希,因为文件没有更改。
在执行此操作时,我需要移动它们或删除它们,但由于文件名会使它对于该工具来说不够“唯一” uniq
,因此如果直接使用管道,这将不起作用。
有什么办法可以做到这一点吗?不使用除 posix 工具(如 awk、bash 等)之外的任何其他工具,也不使用列表或数据库文件?
细节:不,这在技术上并不是重复的这帖子,以及是的,最终目标在技术上是相同的(即使用我在另一篇文章或此处已经描述的方法/情况下删除或移动重复项)。
答案1
使用bash
(这并不是真正的 POSIX 工具,但既然你明确提到了它):
#!/bin/bash
names=( *:* )
printf '%s\n' "${names[@]##*:}" | sort | uniq -c |
while read count hash; do
if [[ $count -gt 1 ]]; then
echo 'Would delete/move these:'
printf '%s\n' *:"$hash"
fi
done
这会将当前目录中包含:
字符的所有名称收集到数组中names
。假设模式*:*
匹配仅有的我们感兴趣的文件,并且没有其他文件具有这样的名称。
的扩展"${names[@]##*:}"
将产生一个仅包含哈希值的列表,我们使用 对其进行排序和计数sort | uniq -c
。
其结果被读入count
循环hash
中while read
,如果计数大于一,我们就知道哈希是重复的。如果散列重复,该模式*:"$hash"
将匹配具有该散列的所有名称。
如果你想删除全部具有重复哈希值的文件,您可以这样做
rm -f ./*:"$hash"
如果您想保留其中一个文件,则可以这样做,例如
dupnames=( ./*:"$hash" )
rm -f "${dupnames[@]:1}"
这会将数组设置dupnames
为匹配的名称,并从文件系统中删除除第一个之外的所有数组。
您可能希望在启用一些调试输出的情况下运行,并使用rm
残疾人首先,直到您确信这确实有效:
#!/bin/bash
names=( *:* )
printf '%s\n' "${names[@]##*:}" | sort | uniq -c |
while read count hash; do
if [[ $count -gt 1 ]]; then
echo 'Would delete/move these:'
dupnames=( ./*:"$hash" )
echo rm -f "${dupnames[@]:1}"
fi
done
sh
上述的POSIX变体:
#!/bin/sh
for name in *:*; do
printf '%s\n' "${name##*:}"
done | sort | uniq -c |
while read count hash; do
if [ "$count" -gt 1 ]; then
echo 'Would delete/move these:'
set -- ./*:"$hash"
shift
echo rm -f "$@"
fi
done
sort | uniq -c
最后一个的一种变体,通过以下方式消除了awk
:
#!/bin/sh
for name in *:*; do
printf '%s\n' "${name##*:}"
done |
awk ' { count[$0]++ }
END { for (hash in count) if (count[hash] > 1) print hash }' |
while read hash; do
echo 'Would delete/move these:'
set -- ./*:"$hash"
shift
echo rm -f "$@"
done
该awk
片段也可以替换sort | uniq -c
此答案中的其他代码片段,但请注意,最终循环现在不需要测试计数是否大于一,并且它只读取哈希值。