如何从Linux Bash上的字符串中计算CRC32校验和



我使用CRC32很久以前从字符串中计算出校验和

echo -n "LongString" | crc32    # no output

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

# 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]如何用Python计算CRC32以匹配在线结果?

我本人遇到了这个问题,我不想去"麻烦"安装crc32。我想到了这个,尽管有点讨厌,它应该在大多数平台上工作,或者大多数现代的Linux ...

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

只是为了提供一些技术细节,GZIP在最近的8个字节中使用CRC32,而-c选项使其输出输出到标准输出,并且tail剥离了最后8个字节。(如@markadler所建议的-1,所以我们不会浪费时间实际进行压缩(。

hexdump有点棘手,我不得不在我提出令人满意的东西之前对它进行了一段时间,但是这里的格式似乎正确地解析了GZIP CRC32作为一个32位编号:

  • -n4仅采用GZIP页脚的相关前4个字节。
  • '"%u"'是您的标准fprintf格式字符串,将字节格式为单个未签名的32位整数。请注意,这里有双引号嵌套在此处的单语

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

就像我说的那样,不是最优雅的解决方案,也许不是您想在性能敏感的情况下使用的方法,而是在使用命令的几乎普遍性的情况下可能会吸引的一种方法。

跨平台可用性的弱点可能是hexdump配置,因为我已经看到了从平台到平台上的变化,而且有点麻烦。我建议如果您正在使用此功能,则应尝试一些测试值并与在线工具的结果进行比较。

编辑 @pedrogimeno在注释中建议,您可以将输出输送到od而不是hexdump中,而不是相同的结果,而无需贴心的选项。... | od -t x4 -N 4 -A n用于十进制的HEX ... | od -t d4 -N 4 -A n

或仅使用过程替代:

crc32 <(echo -n "LongString")

(编辑:thx @tor-klingberg(

您的问题已经具有大多数答案。

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

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

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

cksum命令首先出现在4.4BSD Unix上,应在所有现代系统中都存在。

在ubuntu上,至少/usr/bin/crc32是一个简短的perl脚本,您可以清楚地从其源头看到它所能做的就是打开文件。它没有从Stdin阅读的设施 - 它没有针对-作为文件名的特殊处理,或-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"

请注意&到背景的fifo的过程,因为它将阻止直到下一个命令读取它。

要对临时文件的创建更加挑剔,请参见:https://unix.stackexchange.com/questions/181937/how-create-a-temporrary-file-in-shell-shell-script


另外,以脚本中的内容为例,从中写下您自己的perl单线(系统上的crc32的存在表示已安装Perl和必要的模块(,或使用您使用Python One-Liner'已经找到了。

这是一个纯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: %snis: %08xn' "${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

您可以尝试使用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)

最新更新