目录中唯一文件的计数器

目录中唯一文件的计数器

我多次运行一个程序,其输出(稍微)不确定。每次,我都将输出打印到文件中。我现在有一个包含许多文本文件的目录 (95,034),其中可能有 4 个不同的唯一输出。我希望看到这样的格式的输出:

 A (50,000)
 B (30,000)
 C (10,000)
 D  (5,034)

但即使只是看到 A、B、C、D(四种不同的可能输出)的内容也会很棒。我没有时间手动删除 90,000 个文件的重复数据。那么如何计算或列出目录中唯一的文本文件呢?谢谢!

答案1

我是 GNU 的忠实粉丝datamashhttps://www.gnu.org/software/datamash/)。以下是我创建并运行此命令的一组模拟文件的示例输出:

$ md5sum * | datamash -W -s -g 1 count 2 -f
5591dadf0051bee654ea41d962bc1af0    junk1   27
9c08c31b951a1a1e0c3a38effaca5863    junk2   17
f1e5cbfade7063a0c4fa5083fd36bf1a    junk3   7

有 27 个文件的哈希值为 5591...,其中一个是“junk1”。 (类似地,有 17 个与“junk2”相同的文件,以及 7 个与“junk3”相同的文件)。

表示-W使用空格作为字段分隔符。表示-s -g 1按字段 1(即哈希值)排序和分组。可能count是字段 1 或字段 2,这并不重要。

上面-f说“打印整个输入行”。这有一个怪癖,当您打印聚合结果时,它只打印整行第一的它找到的每个组中的行。在这种情况下效果很好,因为它为我们提供了每个重复集中涉及的文件名之一,而不是全部。

答案2

稍微扩展@Isaac 的解决方案......

假设bash语法,并给出:

$ find test -type f
test/AA
test/A
test/C
test/CC
test/B
test/D

其中文件 A 和 AA 相同,C 和 CC 也相同;

这是一个逐渐更有效的命令管道:

$ find test -maxdepth 1 -type f -exec bash -c "md5sum < {}" \; |
    sort -k1,1 |
    uniq --count
      2 102f2ac1c3266e03728476a790bd9c11  -
      1 4c33d7f68620b7b137c0ca3385cb6597  -
      1 88178a003e2305475e754a7ec21d137d  -
      2 c7a739d5538cf472c8e87310922fc86c  -

现在剩下的问题是 md5 哈希值不会告诉您哪些文件是 A、B、C 或 D。这是可以解决的,尽管有点麻烦。

首先,将文件移至子目录中,或者如果更方便的话,将 PWD 移至上一个目录。在我的示例中,我正在工作.并且文件位于test/.

我建议您分别识别四种文件类型中的一种,并将它们复制到文件 A、B、C 和 D(如果需要,还可复制到文件 Z):

$ cp -p test/file1002 ./A
...
$ cp -p test/file93002 ./N

等等。我们现在可以构建一个哈希表,定义每个唯一输出文件 AZ 的 md5 哈希值:

$ for file in [A-Z]; do 
      printf "s/%s/%s/\n" "$(md5sum < $file )" "$file"; 
done
s/102f2ac1c3266e03728476a790bd9c11  -/A/
s/4c33d7f68620b7b137c0ca3385cb6597  -/B/
s/c7a739d5538cf472c8e87310922fc86c  -/C/
s/88178a003e2305475e754a7ec21d137d  -/D/

请注意,哈希表看起来像sed语法。原因如下:

让我们运行find ... md5sum上面相同的管道:

$ find test -maxdepth 1 -type f -exec bash -c "md5sum < {}" \; |
    sort -k1,1 |
    uniq --count

...并将其通过一个sed使用上面的哈希表的过程进行管道传输,以将哈希值替换为原型文件名。该sed命令本身是:

sed -f <(
    for file in [A-Z]; do 
        printf "s/%s/%s/\n" "$(md5sum < "$file")" "$file"; 
    done
)

因此,将它们连接在一起:

$ find test -maxdepth 1 -type f -exec bash -c "md5sum < {}" \; |
    sort -k1,1 |
    uniq --count |
    sed -f <(
        for file in [A-Z]; do 
            printf "s/%s/%s/\n" "$(md5sum < "$file")" "$file"; 
        done
    )
  2 A
  1 B
  1 D
  2 C

如果您看到这样的输出:

  2 A
  1 B
  1 5efa8621f70e1cad6aba9f8f4246b383  -
  1 D
  2 C

这意味着某个文件的test/MD5 值与您的文件 AD 不匹配。换句话说,E某处存在一种输出文件格式。一旦找到它(md5sum test/* | grep 5efa8621f70e1cad6aba9f8f4246b383),您可以将其复制到 E 并重新运行:

$ cp -p test/file09876 ./E
$ find test -maxdepth 1 -type f -exec bash -c "md5sum < {}" \; |
    sort -k1,1 |
    uniq --count |
    sed -f <(
        for file in [A-Z]; do 
            printf "s/%s/%s/\n" "$(md5sum < "$file")" "$file"; 
        done
    )
  2 A
  1 B
  1 E
  1 D
  2 C

答案3

您也可以使用sortanduniq来实现此目的。从文件所在的文件夹中,输入:

find . -type f | awk '{ print "tr \\\\n @ < " $0 "; echo "}' | sh | sort | uniq --count

(如果不使用GNU coreutils 中的,则替换uniq --count为。)uniq -cuniq

这应该可以一次性给你结果。为了简单和速度(避免散列),我们将换行符转换为@- 这可以是不属于原始文件的任何单个字符。

(这假设子文件夹中的文件(如果存在)将被包含在内。另一个假设是文件中没有@字符。如果没有,请发表评论,我将相应地调整命令。)

答案4

使用哈希映射来收集所有唯一文件。哈希取决于内容,因此只有具有唯一内容的文件才会在哈希映射中获得条目。

declare -A unique_files
for file in *; do 
    unique_files["$(md5sum "$file" | cut -d ' ' -f 1)"]="$file"
done
echo "${unique_files[@]}"

相关内容