Bash에서 ASCII 값 대신 이진 값을 파일에 쓰는 방법

Bash에서 ASCII 값 대신 이진 값을 파일에 쓰는 방법

저는 파일 설명자를 통해 액세스할 수 있는 일부 메모리가 있는 임베디드 시스템에서 작업하고 있습니다. (무슨 말인지 모르겠습니다. 틀렸다면 정정해 주십시오.)

메모리는 32kB이고 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은 32kb가 아닌 128KiB를 차지합니다. 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)을 (unsigned Short)로 대체하여 S0..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

관련 정보