如何在 Bash 中将二进制值而不是 ASCII 值写入文件

如何在 Bash 中将二进制值而不是 ASCII 值写入文件

我正在处理一个嵌入式系统,它有一些可以通过文件描述符访问的内存(我不知道我在说什么,所以如果我错了,请纠正我)。

该内存为 32 kB,我想用 0x00 到 0xFFFFFFFF 填充它。我知道文本文件是这样的:

exec {fh} >> ./eeprom;
for i in {0..32767}; do echo $i >& $fh; done; $fh>&-;

这将写入 ASCII 字符 0 到 977。

如果我这样做,hexdump eeprop | head我会得到:

0000000 0a30 0a31 0a32 0a33 0a34 0a35 0a36 0a37
0000010 0a38 0a39 3031 310a 0a31 3231 310a 0a33
0000020 3431 310a 0a35 3631 310a 0a37 3831 310a
0000030 0a39 3032 320a 0a31 3232 320a 0a33 3432
0000040 320a 0a35 3632 320a 0a37 3832 320a 0a39
0000050 3033 330a 0a31 3233 330a 0a33 3433 330a
0000060 0a35 3633 330a 0a37 3833 330a 0a39 3034
0000070 340a 0a31 3234 340a 0a33 3434 340a 0a35
0000080 3634 340a 0a37 3834 340a 0a39 3035 350a
0000090 0a31 3235 350a 0a33 3435 350a 0a35 3635

如何用它的uint32而非 ASCII 表示形式填充每个地址?

答案1

perl -e 'print pack "L*", 0..0x7fff' > file

将以本地系统的字节顺序写入它们。

使用:

perl -e 'print pack "L>*", 0..0x7fff'
perl -e 'print pack "L<*", 0..0x7fff'

无论本地系统的本机字节序如何,分别强制使用大端字节序或小端字节序。

perldoc -f pack详情请参阅。

特别是使用 bash 内置函数,您可以编写任意内容字节值与:

printf '\123' # 123 in octal
printf '\xff' # ff in hexadecimal

因此,您可以通过手动写入 uint32 数字的每个字节来完成此操作,如下所示:

for ((i = 0; i <= 32767; i++)); do
  printf -v format '\\x%x' \
    "$((         i & 0xff ))" \
    "$(( (i >>  8) & 0xff ))" \
    "$(( (i >> 16) & 0xff ))" \
    "$(( (i >> 24) & 0xff ))"
  printf "$format"
done

(此处为小端)。

无论如何,请注意 32767 是 0x7fff,而不是0xFFFFFFFF。 uint32 数字 0 到 32767 占用 128KiB,而不是 32kb。 0 到 0xFFFFFFFF 将占用 16GiB。

要将这些 16GiB 写入 中perl,您需要将代码更改为:

perl -e 'print pack "L", $_ for 0..0xffffffff'

否则它会尝试(并且可能会失败)在内存中分配这 16GiB。在我的系统上,我发现perl写入输出的速度约为 30MiB/s,而bash写入的速度约为 250KiB/s(因此需要几个小时才能完成)。

要写入 32kb(32000 位、4000 字节、1000 个 uint32 数字)的 uint32 数字,您可以使用 0..999 范围。 32KiB 为 0..8191。或者您可以通过将L(unsigned long) 替换为S(unsigned Short)将 0..16383 写为 uint16 数字。

答案2

使用C。这会比Perl 稍快一些。

#include <unistd.h>
// in bytes, and MUST be sizeof(int) * somevalue
#define BSZ 4096

int main(void) {
    // TWEAK how far to go up to (set below 0xFFFFFFFF to actually
    // be testable on the 2009 macbook with a spinny metal drive and
    // no main CPU fan. the other laptop is even older and slower)
    // NOTE that max MUST fit exactly into a full buf
    unsigned long max = 0xFFFFFF;

    int buf[BSZ];
    unsigned long buf_size = BSZ;
    unsigned long buf_nint = BSZ / sizeof(int);
    unsigned long total    = 0;

    while (total < max) {
        unsigned long i = 0;
        for (; i < buf_nint; i++) {
            // TWEAK on little-endian you can htonl(3) this
            // to get one of the other byte orders, if you
            // need that. other byte orders on other systems
            // will be more complicated
            buf[i] = (int) (total + i);
        }
        write(STDOUT_FILENO, buf, buf_size);
        total += i;
    }
    return 0;
}

你仍然可以使用 Perl 来确认 C 并没有太可怕:

% perl -e 'print pack "L", $_ for 0..0xffffff' > p.out
% cc -O2 -std=c99 incrnumberfill.c -o incrnumberfill
% ./incrnumberfill > c.out
% cmp {p,c}.out
% echo $?
0
% repeat 3; time perl -e 'print pack "L", $_ for 0..0xffffff' > /dev/null
perl -e 'print pack "L", $_ for 0..0xffffff' > /dev/null  4.11s user 0.02s system 99% cpu 4.151 total
perl -e 'print pack "L", $_ for 0..0xffffff' > /dev/null  4.43s user 0.02s system 99% cpu 4.471 total
perl -e 'print pack "L", $_ for 0..0xffffff' > /dev/null  4.38s user 0.02s system 99% cpu 4.420 total
% repeat 3; time ./incrnumberfill > /dev/null
./incrnumberfill > /dev/null  0.02s user 0.01s system 88% cpu 0.035 total
./incrnumberfill > /dev/null  0.02s user 0.01s system 86% cpu 0.035 total
./incrnumberfill > /dev/null  0.02s user 0.01s system 88% cpu 0.034 total

相关内容