如何从 linux bash 上的字符串计算 crc32 校验和

Posted

技术标签:

【中文标题】如何从 linux bash 上的字符串计算 crc32 校验和【英文标题】:How to calculate crc32 checksum from a string on linux bash 【发布时间】:2017-12-01 22:43:35 【问题描述】:

很久以前我就使用 crc32 从字符串中计算校验和,但我不记得我是怎么做到的了。

echo -n "LongString" | crc32    # no output

我找到了一个解决方案 [1] 来用 Python 计算它们,但是没有直接的方法可以从字符串中计算出来吗?

# signed
python -c 'import binascii; print binascii.crc32("LongString")'
python -c 'import zlib; print zlib.crc32("LongString")'
# unsigned
python -c 'import binascii; print binascii.crc32("LongString") % (1<<32)'
python -c 'import zlib; print zlib.crc32("LongString") % (1<<32)'

[1]How to calculate CRC32 with Python to match online results?

【问题讨论】:

【参考方案1】:

我自己遇到了这个问题,我不想去安装crc32 的“麻烦”。我想出了这个,虽然它有点讨厌,但它应该可以在大多数平台上运行,或者在大多数现代 linux 上运行......

echo -n "LongString" | gzip -c | tail -c8 | hexdump -n4 -e '"%u"'

只是为了提供一些技术细节,gzip uses crc32 在最后 8 个字节中,-c 选项使其输出到标准输出,tail 去除最后 8 个字节。

hexdump 有点棘手,在我想出令人满意的东西之前,我不得不折腾了一段时间,但这里的格式似乎正确地将 gzip crc32 解析为单个 32 位数字:

-n4 只占用 gzip 页脚的相关前 4 个字节。 '"%u"' 是您的标准 fprintf 格式字符串,它将字节格式化为单个无符号 32 位整数。 请注意这里的单引号中嵌套了双引号

如果您需要十六进制校验和,您可以将格式字符串更改为 '"%08x"'(或 '"%08X"' 为大写十六进制),这会将校验和格式化为 8 个字符(0 填充)十六进制。

就像我说的那样,这不是最优雅的解决方案,也可能不是您希望在性能敏感的场景中使用的方法,但考虑到所用命令的近乎普遍性,这种方法可能会吸引人。

这里跨平台可用性的弱点可能是hexdump 配置,因为我看到它在不同平台之间存在差异,而且有点繁琐。我建议如果您正在使用它,您应该尝试一些测试值并与results of an online tool 进行比较。

编辑 正如@PedroGimeno 在 cmets 中所建议的那样,您可以将输出通过管道传输到 od 而不是 hexdump 以获得相同的结果,而无需繁琐的选项。 ... | od -t x4 -N 4 -A n 十六进制 ... | od -t d4 -N 4 -A n 十进制。

【讨论】:

一个更便携的十六进制解决方案是使用od而不是hexdump:... | od -t x4 -N 4 -A n 可以确认这是一种享受! -t x4 用于十六进制输出,-t d4 用于十进制。 使用gzip -1 -c 加快压缩速度,因为无论如何你都要把它扔掉。【参考方案2】:

或者只使用进程替换:

crc32 <(echo "LongString")

【讨论】:

我一直在寻找这个也可以使用 pv。在生成进度条的同时将文件输出为字符串。 crc32 &lt;(pv /some/file) 完美运行。 如果你想让你的管道从左到右你可以做echo -n "LongString" | crc32 /dev/stdin。 /dev/stdin 是一个包含进程输入的特殊文件。【参考方案3】:

您的问题已经有了大部分答案。

echo -n 123456789 | python -c 'import sys;import zlib;print(zlib.crc32(sys.stdin.read())%(1<<32))'

正确给出3421780262

我更喜欢十六进制:

echo -n 123456789 | python -c 'import sys;import zlib;print("%08x"%(zlib.crc32(sys.stdin.read())%(1<<32)))'
cbf43926

请注意,有多种 CRC-32 算法: http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.cat-bits.32

【讨论】:

有趣的是,其中列出的没有一个使用 EDB88320 的“ZIP”多边形 @silverdr 所有带有poly=0x04c11db7refin=true 的人都可以。那里列出的 CRC-32/ISO-HDLC 是 PKZIP CRC。 我必须在这里遗漏一些明显的东西,但是poly=0x04c11db7 是什么意思使用edb88320?我想这与refin=true 有关?诚实的问题,因为我正在寻找适应校验和例程所需的定义并发现(对我而言)相互矛盾的信息。最终最终使用 edb88320 与起始种子 ffffffff 和最终 ffffffff EOR 以获得与上述 crc32 脚本输出兼容的结果。 @silverdr 0xedb883200x04c11db7 的位反转。 refin=true 表示输入位被反映。实际上,这永远不会完成,因为您必须对每个输入字节都这样做。相反,多项式被反射一次。 Python 3:| python3 -c 'import sys;import zlib;print(":x".format(zlib.crc32(sys.stdin.buffer.read())%(1&lt;&lt;32)))' 【参考方案4】:

至少在 Ubuntu 上,/usr/bin/crc32 是一个简短的 Perl 脚本,您可以从它的源代码中非常清楚地看到它所能做的就是打开文件。它没有从标准输入读取的功能——它没有对 - 作为文件名、-c 参数或类似的东西进行特殊处理。

所以你最简单的方法是接受它,并制作一个临时文件。

tmpfile=$(mktemp)
echo -n "LongString" > "$tmpfile"
crc32 "$tmpfile"
rm -f "$tmpfile"

如果你真的不想写一个文件(例如,它的数据比你的文件系统可以接受的多——如果它真的是一个“长字符串”则不太可能,但为了争论......)你可以使用命名管道。对于简单的非随机访问阅读器来说,这与文件无法区分:

fifo=$(mktemp -u)
mkfifo "$fifo"
echo -n "LongString" > "$fifo" &
crc32 "$fifo"
rm -f "$fifo"

注意&amp; 将写入fifo 的进程置于后台,因为它会阻塞直到下一个命令读取它。

要更加注意临时文件的创建,请参阅:https://unix.stackexchange.com/questions/181937/how-create-a-temporary-file-in-shell-script


或者,使用脚本中的内容作为示例,从中编写您自己的 Perl 单行代码(系统上存在 crc32 表示已安装 Perl 和必要的模块),或使用 Python one-你已经找到了。

【讨论】:

这也适用于为您处理 FIFO:crc32 【参考方案5】:

我使用cksum 并使用shell 内置printf 转换为十六进制:

$ echo -n "LongString"  | cksum | cut -d\  -f1 | xargs echo printf '%0X\\n' | sh
5751BDB2

【讨论】:

我必须使用cut -d" " -f1 而不是cut -d\ -f1 (所以在这里修剪两个空格之一)否则只会出错。【参考方案6】:

这是一个纯 Bash 实现:

#!/usr/bin/env bash

declare -i -a CRC32_LOOKUP_TABLE

__generate_crc_lookup_table() 
  local -i -r LSB_CRC32_POLY=0xEDB88320 # The CRC32 polynomal LSB order
  local -i index byte lsb
  for index in 0..255; do
    ((byte = 255 - index))
    for _ in 0..7; do # 8-bit lsb shift
      ((lsb = byte & 0x01, byte = ((byte >> 1) & 0x7FFFFFFF) ^ (lsb == 0 ? LSB_CRC32_POLY : 0)))
    done
    ((CRC32_LOOKUP_TABLE[index] = byte))
  done

__generate_crc_lookup_table
typeset -r CRC32_LOOKUP_TABLE

crc32_string() 
  [[ $# -eq 1 ]] || return
  local -i i byte crc=0xFFFFFFFF index
  for ((i = 0; i < $#1; i++)); do
    byte=$(printf '%d' "'$1:i:1") # Get byte value of character at i
    ((index = (crc ^ byte) & 0xFF, crc = (CRC32_LOOKUP_TABLE[index] ^ (crc >> 8)) & 0xFFFFFFFF))
  done
  echo $((crc ^ 0xFFFFFFFF))


printf 'The CRC32 of: %s\nis: %08x\n' "$1" "$(crc32_string "$1")"

# crc32_string "The quick brown fox jumps over the lazy dog"
# yields 414fa339

测试:

bash ./crc32.sh "The quick brown fox jumps over the lazy dog"
The CRC32 of: The quick brown fox jumps over the lazy dog
is: 414fa339

【讨论】:

【参考方案7】:

您可以尝试使用rhash

http://rhash.sourceforge.net/ https://github.com/rhash/RHash http://manpages.ubuntu.com/manpages/bionic/man1/rhash.1.html

测试:

## install 'rhash'...
$ sudo apt-get install rhash
## test CRC32...
$ echo -n 123456789 | rhash --simple -
cbf43926  (stdin)

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于如何从 linux bash 上的字符串计算 crc32 校验和的主要内容,如果未能解决你的问题,请参考以下文章

Linux上的10个超级方便的Bash别名

Linux和Windows文件格式为啥要转换以及如何转换

Linux系统入门-Bash

如何规范化 Java 中的 EOL 字符?

如何从 Bash 脚本中检测操作系统?

如何从 Mac 上的 bash 脚本调用 ./snowsql 文件?