나는 여러 가지 이유로 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에서 이 작업을 수행하는 더 좋은 방법이 있습니까? 기본 바이너리의 바이너리 바이트를 복사/읽기하여 파일에 그대로 복사할 수 있나요? (바람직하게는 변수로도 저장됩니다).
답변1
일반적으로 쉘 스크립트에서 낮은 수준의 바이너리 데이터를 처리하는 것은 좋지 않습니다.
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바이트로 인코딩하므로 공간 효율적이지 않지만 작업(부분 연결, 추출)을 더 쉽게 수행할 수 있습니다. base64
3바이트를 4바이트로 인코딩하는 방식인데 사용이 쉽지 않습니다.
셸에는 해당 및 / 유틸리티 와 함께 사용할 수 있는 ksh93
내장 인코딩 형식(사용)이 있습니다 .base64
read
printf
print
typeset -b var # marked as "binary"/"base64-encoded"
IFS= read -rn 988 var < input
printf %B var > output
이제 쉘이나 환경 변수 또는 명령 매개변수를 통한 전송이 없는 경우 사용하는 유틸리티가 모든 바이트 값을 처리할 수 있다면 괜찮을 것입니다. 그러나 텍스트 유틸리티의 경우 GNU가 아닌 대부분의 구현은 NUL 바이트를 처리할 수 없으며 멀티바이트 문자 문제를 방지하려면 로케일을 C로 수정해야 합니다. 개행 문자가 아닌 마지막 문자는 매우 긴 줄뿐만 아니라 문제를 일으킬 수도 있습니다(두 개의 0xa 바이트 사이의 바이트 시퀀스가 그보다 깁니다 LINE_MAX
).
head -c
사용 가능한 경우 바이트로 작업하고 데이터를 텍스트로 처리할 이유가 없기 때문에 여기서는 괜찮을 것입니다. 그래서
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
답변2
나는 여러 가지 이유로 C++ 코드를 bash로 야심차게 변환하려고 합니다.
예. 하지만 아마도 이렇게 하지 말아야 할 매우 중요한 이유를 생각해 봐야 할 것입니다. 기본적으로 "bash"/"sh"/"csh"/"ksh" 등은 바이너리 데이터를 처리하도록 설계되지 않았으며 대부분의 표준 UNIX/LINUX 유틸리티도 마찬가지입니다.
C++를 고수하거나 Python, Ruby 또는 Perl과 같은 바이너리 데이터를 처리할 수 있는 스크립팅 언어를 사용하는 것이 더 좋습니다.
Bash에서 이 작업을 수행하는 더 좋은 방법이 있습니까?
더 나은 접근 방식은 bash에서 이 작업을 수행하지 않는 것입니다.
답변3
귀하의 질문에서 :
헤더의 처음 988줄을 복사합니다.
988행을 복사하면 바이너리 파일이 아닌 텍스트 파일처럼 보입니다. 그러나 귀하의 코드는 988라인이 아닌 988바이트를 가정하는 것으로 보이므로 바이트가 정확하다고 가정합니다.
hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
이 부분이 작동하지 않을 수 있습니다. 우선, 스트림의 모든 NUL 바이트는 ${hdr_988}
명령줄 인수로 사용하고 명령줄 인수는 NUL을 포함할 수 없기 때문에 제거됩니다. 백틱은 공백 처리도 수행할 수 있습니다(이에 대해서는 잘 모르겠습니다). (실제로 echo
내장되어 있기 때문에 NUL 제한은가능한해당되지는 않지만 아직 확실하지 않다고 말하고 싶습니다. )
헤더를 쉘 변수를 통해 전달하는 대신 입력 파일에서 출력 파일로 직접 작성하면 어떨까요?
head -c 988 "${inputFile}" >"${output_hdr}"
또는 더 휴대하기 좋게,
dd if="${inputFile}" of="${output_hdr}" bs=988 count=1
bash
POSIX 쉘 대신에 를 사용한다고 말씀하셨는데 , 프로세스 대체를 사용할 수 있는데, 테스트로 사용해 보시는 건 어떨까요?
cmp <(head -c 988 "${inputFile}") <(head -c 988 "${output_hdr}")
마침내:고려하다대신 백틱을 사용하세요 $( ... )
.