如何仅部分匹配文件名的一个实例?

如何仅部分匹配文件名的一个实例?

所以我有一个文件列表,我将其重命名为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循环hashwhile 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此答案中的其他代码片段,但请注意,最终循环现在不需要测试计数是否大于一,并且它只读取哈希值。

相关内容