如何从命令行有效地计算一堆文件块的 SHA1 总和?
How can I efficiently compute the SHA1 sums of a bunch of chunks of a file from the command line?
我有一个文件 f,我想在该文件上计算一系列 SHA1 哈希值,每个 2 kB 块对应一个哈希值。例如,如果 f 的大小为 1G,我想计算大约 50 万个哈希值:字节 0-2047、字节 2048-4095、字节 4096-6143、字节 6144-8192 等各一个。理想情况下,我想通过一次读取文件并将结果流式传输到另一个文件或管道来完成这一切。我知道 split
和 sha1sum
命令,但我不确定如何将它们与其他命令结合起来以实现此目标。是否有类似 split
的东西可以避免创建中间文件并对计算这些 SHA1 和有用?
如果我在较低级别实现它,我当然会按顺序将文件读入一个小缓冲区,在为每个新块重新使用缓冲区之前,我会从中计算 SHA1 总和,但我更喜欢简单 bash 一行,如果有的话。在 bash 中有没有好的方法来做到这一点(假设 f 可以包含空字节)?
这是一个使用 Python's hashlib
的解决方案。它足够小,可以合理地内嵌在 shell 脚本中。
#!/bin/bash -eu
python <<EOF
import hashlib
chunksize = 2048
md = hashlib.sha1()
with open("", 'rb') as istr:
while True:
chunk = istr.read(chunksize)
if not chunk:
break
md.update(chunk)
print(md.hexdigest())
EOF
我使用存储在 ramdisk 中的 1 GiB 文件对其计时。 sha1sum
命令行工具用了 3.24 秒来散列整个文件。上面的脚本(标准输出通过管道传输到 /dev/null
)花费了 7.35 秒,大约是原来的两倍。这对我来说非常好,特别是如果考虑到它实际上做了更多的工作。
请注意,这会计算累积哈希值(每个摘要涵盖所有先前的字节)。阅读其他答案后,我不再确定这是否是您想要的。如果您想要独立的摘要,则必须将 md = hashlib.sha1()
移动到循环内。在这种情况下,上述基准测试需要 8.26 秒。
所以,这种方法适用于我手边的机器:
(
file=large-file.txt
size=$(stat -c %s "$file")
for (( i = 0 ; i * 2048 < size ; ++i )) ; do
head -c 2k | sha1sum -
done < "$file"
)
或单线:
( file=large-file.txt ; size=$(stat -c %s "$file") ; for (( i = 0 ; i * 2048 < size ; ++i )) ; do ; head -c 2k | sha1sum - ; done < "$file" )
在我的机器上,for 循环的每次迭代只读取前 2KB 并将它们传递给 sha1sum
,将文件的其余部分留给下一次迭代。
但是,我在 head
的文档中没有看到 承诺 这种行为的任何内容。 (head -c 2k
被记录为只打印 2 KB,但这并不一定意味着它只会从标准输入中消耗 2 KB。)所以虽然它至少在一个盒子上工作,但我不能保证它是可移植到您可能想要使用它的所有盒子。 (就此而言,-c
标志本身并未由 POSIX 指定;相反,它是 GNU 实现的一个特性。)
我有一个文件 f,我想在该文件上计算一系列 SHA1 哈希值,每个 2 kB 块对应一个哈希值。例如,如果 f 的大小为 1G,我想计算大约 50 万个哈希值:字节 0-2047、字节 2048-4095、字节 4096-6143、字节 6144-8192 等各一个。理想情况下,我想通过一次读取文件并将结果流式传输到另一个文件或管道来完成这一切。我知道 split
和 sha1sum
命令,但我不确定如何将它们与其他命令结合起来以实现此目标。是否有类似 split
的东西可以避免创建中间文件并对计算这些 SHA1 和有用?
如果我在较低级别实现它,我当然会按顺序将文件读入一个小缓冲区,在为每个新块重新使用缓冲区之前,我会从中计算 SHA1 总和,但我更喜欢简单 bash 一行,如果有的话。在 bash 中有没有好的方法来做到这一点(假设 f 可以包含空字节)?
这是一个使用 Python's hashlib
的解决方案。它足够小,可以合理地内嵌在 shell 脚本中。
#!/bin/bash -eu
python <<EOF
import hashlib
chunksize = 2048
md = hashlib.sha1()
with open("", 'rb') as istr:
while True:
chunk = istr.read(chunksize)
if not chunk:
break
md.update(chunk)
print(md.hexdigest())
EOF
我使用存储在 ramdisk 中的 1 GiB 文件对其计时。 sha1sum
命令行工具用了 3.24 秒来散列整个文件。上面的脚本(标准输出通过管道传输到 /dev/null
)花费了 7.35 秒,大约是原来的两倍。这对我来说非常好,特别是如果考虑到它实际上做了更多的工作。
请注意,这会计算累积哈希值(每个摘要涵盖所有先前的字节)。阅读其他答案后,我不再确定这是否是您想要的。如果您想要独立的摘要,则必须将 md = hashlib.sha1()
移动到循环内。在这种情况下,上述基准测试需要 8.26 秒。
所以,这种方法适用于我手边的机器:
(
file=large-file.txt
size=$(stat -c %s "$file")
for (( i = 0 ; i * 2048 < size ; ++i )) ; do
head -c 2k | sha1sum -
done < "$file"
)
或单线:
( file=large-file.txt ; size=$(stat -c %s "$file") ; for (( i = 0 ; i * 2048 < size ; ++i )) ; do ; head -c 2k | sha1sum - ; done < "$file" )
在我的机器上,for 循环的每次迭代只读取前 2KB 并将它们传递给 sha1sum
,将文件的其余部分留给下一次迭代。
但是,我在 head
的文档中没有看到 承诺 这种行为的任何内容。 (head -c 2k
被记录为只打印 2 KB,但这并不一定意味着它只会从标准输入中消耗 2 KB。)所以虽然它至少在一个盒子上工作,但我不能保证它是可移植到您可能想要使用它的所有盒子。 (就此而言,-c
标志本身并未由 POSIX 指定;相反,它是 GNU 实现的一个特性。)