我多次运行一个程序,其输出(稍微)不确定。每次,我都将输出打印到文件中。我现在有一个包含许多文本文件的目录 (95,034),其中可能有 4 个不同的唯一输出。我希望看到这样的格式的输出:
A (50,000)
B (30,000)
C (10,000)
D (5,034)
但即使只是看到 A、B、C、D(四种不同的可能输出)的内容也会很棒。我没有时间手动删除 90,000 个文件的重复数据。那么如何计算或列出目录中唯一的文本文件呢?谢谢!
答案1
我是 GNU 的忠实粉丝datamash
(https://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
您也可以使用sort
anduniq
来实现此目的。从文件所在的文件夹中,输入:
find . -type f | awk '{ print "tr \\\\n @ < " $0 "; echo "}' | sh | sort | uniq --count
(如果不使用GNU coreutils 中的,则替换uniq --count
为。)uniq -c
uniq
这应该可以一次性给你结果。为了简单和速度(避免散列),我们将换行符转换为@
- 这可以是不属于原始文件的任何单个字符。
(这假设子文件夹中的文件(如果存在)将被包含在内。另一个假设是文件中没有@
字符。如果没有,请发表评论,我将相应地调整命令。)
答案4
使用哈希映射来收集所有唯一文件。哈希取决于内容,因此只有具有唯一内容的文件才会在哈希映射中获得条目。
declare -A unique_files
for file in *; do
unique_files["$(md5sum "$file" | cut -d ' ' -f 1)"]="$file"
done
echo "${unique_files[@]}"