bash에서 바이너리로 작업하여 변환없이 바이트를 그대로 복사하려면 어떻게해야합니까?


14

나는 무수한 이유로 C ++ 코드를 bash로 번역하려고 애매하게 노력하고 있습니다.

이 코드는 바이너리로 완전히 작성되고 구조화 된 내 서브 필드에 특정한 파일 유형을 읽고 조작합니다. 첫 번째 이진 관련 작업은 헤더의 첫 988 바이트를 그대로 그대로 복사 한 다음 나머지 정보를 생성 할 때 계속 쓸 수있는 출력 파일에 넣는 것입니다.

현재 솔루션이 작동하지 않는다고 확신하며 실제로이를 결정하는 좋은 방법을 찾지 못했습니다. 실제로 올바르게 작성 되었더라도이를 테스트하기 위해 어떻게 테스트해야하는지 알아야합니다!

이것이 내가 지금하고있는 일입니다.

hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
headInput=`head -c 988 ${inputTrack} | hexdump`
headOutput=`head -c 988 ${output_hdr} | hexdump`
if [ "${headInput}" != "${headOutput}" ]; then echo "output header was not written properly.  exiting.  please troubleshoot."; exit 1; fi

hexdump / xxd를 사용하여 파일 의이 부분을 확인하면 대부분을 읽을 수는 없지만 문제가있는 것 같습니다. 그리고 비교를 위해 작성한 코드는 두 문자열이 동일한 지 여부 만 알려주고 원하는 방식으로 복사되지는 않습니다.

bash에서 이것을 수행하는 더 좋은 방법이 있습니까? 파일을 그대로 그대로 복사하기 위해 이진 바이트를 기본 이진으로 복사 / 읽을 수 있습니까? (그리고 변수로 저장하는 것이 이상적임).


당신은 사용할 수 있습니다 dd(그 설정 개별 바이트 복사 count에를 1). 그래도 저장에 대해 잘 모르겠습니다.
DDPWNAGE

C 방식으로 배쉬를하지 마십시오. 많은 두통이 생길 것입니다. 대신 적절한 배쉬 구조를 사용하십시오
Ferrybig

답변:


22

쉘 스크립트에서 낮은 수준의 바이너리 데이터를 다루는 것은 일반적으로 나쁜 생각입니다.

bash변수는 바이트 0을 포함 할 수 없습니다 zsh. 변수에 해당 바이트를 저장할 수있는 유일한 쉘입니다.

어쨌든 명령 인수 및 환경 변수는 execve시스템 호출에 전달되는 NUL 구분 문자열이므로 해당 바이트를 포함 할 수 없습니다 .

또한 다음을 참고하십시오.

var=`cmd`

또는 현대적인 형태 :

var=$(cmd)

의 출력에서 ​​모든 후행 줄 바꿈 문자를 제거합니다 cmd. 따라서 이진 출력이 0xa 바이트로 끝나면에 저장 될 때 엉망이됩니다 $var.

여기에서로 인코딩 된 데이터를 저장해야합니다 xxd -p.

hdr_988=$(head -c 988 < "$inputFile" | xxd -p)
printf '%s\n' "$hdr_988" | xxd -p -r > "$output_hdr"

다음과 같은 도우미 기능을 정의 할 수 있습니다.

encode() {
  eval "$1"='$(
    shift
    "$@" | xxd -p  -c 0x7fffffff
    exit "${PIPESTATUS[0]}")'
}

decode() {
  printf %s "$1" | xxd -p -r
}

encode var cat /bin/ls &&
  decode "$var" | cmp - /bin/ls && echo OK

xxd -p출력은 1 바이트를 2 바이트로 인코딩하므로 공간 효율적이지 않지만 조작을보다 쉽게 ​​수행 할 수 있습니다 (연결, 추출). base644에서 3 바이트를 인코딩하지만 작업하기 쉽지 않습니다.

ksh93쉘은 형식 (용도를 코딩하는 내장이 base64당신이 그와 함께 사용할 수있는) readprintf/ print유틸리티 :

typeset -b var # marked as "binary"/"base64-encoded"
IFS= read -rn 988 var < input
printf %B var > output

이제 쉘 또는 env 변수 또는 명령 인수를 통한 전달이 없으면 사용하는 유틸리티가 모든 바이트 값을 처리 할 수있는 한 괜찮습니다. 그러나 텍스트 유틸리티의 경우 대부분의 비 GNU 구현은 NUL 바이트를 처리 할 수 ​​없으며 멀티 바이트 문자의 문제점을 피하기 위해 로케일을 C로 수정해야합니다. 줄 바꿈 문자가 아닌 마지막 문자는 또한 매우 긴 행 (두 개의 0xa 바이트 사이에서 더 긴 바이트 순서)뿐만 아니라 문제를 일으킬 수 있습니다 LINE_MAX.

head -c사용 가능한 곳은 바이트로 작업하기위한 것이므로 여기에서 OK이어야하며 데이터를 텍스트로 취급 할 이유가 없습니다. 그래서

head -c 988 < input > output

괜찮을거야. 실제로 적어도 GNU, FreeBSD 및 ksh93 내장 구현은 괜찮습니다. POSIX는 지정하지 않는 -c옵션을하지만 말한다 head길이의 라인을 지원해야한다 (이에 국한되지 LINE_MAX)

zsh:

IFS= read -rk988 -u0 var < input &&
print -rn -- $var > output

또는:

var=$(head -c 988 < input && echo .) && var=${var%.}
print -rn -- $var > output

에서조차도 NUL 바이트를 포함하는 zsh경우 $var, 실행 파일에 전달 된 인수가 NUL로 구분 된 문자열이므로 쉘과는 독립적으로 커널 제한이므로, 위와 같이 zsh내장 print또는 함수에 인수로 전달할 수 있지만 실행 파일에 대한 인수로는 전달할 수 없습니다.


zsh쉘 변수에 하나 이상의 NUL 바이트를 저장할 수있는 유일한 쉘은 아닙니다. ksh93그렇게 할 수도 있습니다. 내부적으로 ksh93이진 변수를 base64로 인코딩 된 문자열로 저장합니다.
fpmurphy

@ fpmurphy1, 이진 데이터 처리 라고 부르는 것이 아닙니다. 변수에는 이진 데이터가 포함되어 있지 않으므로 쉘 연산자를 사용할 수 없습니다. 예를 들어 내장 연산자 또는 함수에 전달할 수 없습니다 디코딩 된 형식 ... 나는 기본적으로 base64 인코딩 / 디코딩 지원 이라고 부릅니다 .
Stéphane Chazelas

11

나는 무수한 이유로 C ++ 코드를 bash로 번역하려고 애매하게 노력하고 있습니다.

그래 그러나 당신은 그것을하지 않는 매우 중요한 이유를 고려해야합니다. 기본적으로 "bash"/ "sh"/ "csh"/ "ksh"등은 이진 데이터를 처리하도록 설계되지 않았으며 표준 UNIX / LINUX 유틸리티 중 대부분이 아닙니다.

C ++를 고수하거나 이진 데이터를 처리 할 수있는 Python, Ruby 또는 Perl과 같은 스크립팅 언어를 사용하는 것이 좋습니다.

bash에서 이것을 수행하는 더 좋은 방법이 있습니까?

더 좋은 방법은 bash에서하지 않는 것입니다.


4
"가장 좋은 방법은 bash에서하지 않는 것입니다."
Guntram Blohm은 Monica

1
이 경로를 사용하지 않는 또 다른 이유는 결과 응용 프로그램이 상당히 느리게 실행되고 더 많은 시스템 리소스를 소비하기 때문입니다.
fpmurphy

배쉬 파이프 라인은 이해를 높일 수있는 고급 도메인 특정 종류의 언어로 작동 할 수 있습니다. 바이너리가 아닌 파이프 라인에 대해 아무것도 및 명령 행 도구로 구현 다양한 유틸리티가있다 바이너리 데이터와 상호 작용 ( ffmpeg, imagemagick, dd). 이제 하나의 것들을 함께 붙이기보다는 프로그래밍을 하고 있다면 완전한 프로그래밍 언어를 사용하는 것이 좋습니다.
Att Righ

6

귀하의 질문에서 :

헤더의 첫 번째 988 줄을 복사하십시오.

988 줄을 복사하는 경우 바이너리가 아닌 텍스트 파일처럼 보입니다. 그러나 코드는 988 줄이 아닌 988 바이트를 가정하는 것으로 보이므로 바이트가 올바른 것으로 가정합니다.

hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}

이 부분은 작동하지 않을 수 있습니다. 우선, ${hdr_988}명령 줄 인수로 사용하고 명령 줄 인수에 NUL을 포함 할 수 없기 때문에 스트림의 모든 NUL 바이트가 제거됩니다 . 백틱은 공백 제거를 수행하고있을 수도 있습니다 (확실하지 않습니다). (실제로 echo내장되어 있기 때문에 NUL 제한 적용되지 않을 수 있지만 여전히 만족 스럽습니다.)

왜 쉘 변수를 거치지 않고 입력 파일에서 출력 파일로 헤더를 직접 쓰지 않겠습니까?

head -c 988 "${inputFile}" >"${output_hdr}"

또는 더 이식 가능

dd if="${inputFile}" of="${output_hdr}" bs=988 count=1

bashPOSIX 셸이 아닌을 사용한다고 언급 했으므로 프로세스 대체가 가능하므로 테스트로는 어떻습니까?

cmp <(head -c 988 "${inputFile}") <(head -c 988 "${output_hdr}")

마지막으로 백틱 대신에 사용 을 고려하십시오$( ... ) .


참고 dd로 반드시 일치하지 않는 head비정규직 파일. 988 바이트를 얻기 위해 필요한 head만큼 많은 read(2)시스템 호출을 dd수행 하는 반면 하나만 수행 할 것 read(2)입니다. GNU ddiflag=fullblock그 블록을 완전히 읽으려고 노력하고 있지만, 그보다 이식성이 떨어집니다 head -c.
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.