Bash에서 널 바이트를 사용하는 방법은 무엇입니까?

Bash에서 널 바이트를 사용하는 방법은 무엇입니까?

Bash의 파일 경로에는 $'\0'null 바이트(값이 0인 바이트)를 제외한 모든 문자가 포함될 수 있으므로 null 바이트를 구분 기호로 사용하는 것이 가장 좋습니다. 예를 들어, 의 출력을 find다른 프로그램으로 보내려면 이 -print0옵션( find이 옵션이 있는 버전의 경우)을 사용하는 것이 좋습니다.

그러나 이와 같은 작업은 잘 작동하지만(줄바꿈으로 구분된 파일 경로 인쇄 - 걱정하지 마세요. 이것은 단지 데모일 뿐이며 실제 스크립트에서는 실제로 이 작업을 수행하지 않습니다):

find -print0 \
  | while IFS= read -r -d $'\0' ; do echo "$REPLY" ; done

이 같은아니요일하다:

for file in * ; do echo -n "$file"$'\0' ; done \
  | while IFS= read -r -d $'\0' ; do echo "$REPLY" ; done

for-loop 부분 만 사용하려고 하면 모든 파일 이름이 함께 인쇄되는 것을 발견했습니다.아니요그 사이에는 널 바이트가 있습니다.

왜 이런거야? 어떻게 되어가나요?

답변1

Bash는 내부적으로 널바이트로 끝나는 C 스타일 문자열을 사용합니다. 즉, Bash 문자열(예: 변수 값 또는 명령 인수)에는 실제로 null 바이트가 포함될 수 없습니다. 예를 들어, 다음 미니 스크립트는 다음과 같습니다.

foobar=$'foo\0bar'    # foobar='foo' + null byte + 'bar'
echo "${#foobar}"     # print length of $foobar

실제로 는 문자열 끝 뒤에 나타나는 : 이기 3때문에 실제로 인쇄됩니다 .$foobar'foo'bar

다시 말하지만, 그 부분은 알려지지 않았기 때문에 echo $'foo\0bar'그냥 인쇄하세요 .fooecho\0bar

보시다시피, 이 시퀀스는 \0실제로 -style 문자열에서 매우 오해의 소지가 있습니다. $'...'문자열에서 널 바이트처럼 보이지만 결국 그런 식으로 작동하지는 않습니다. 첫 번째 예에서 귀하의 read명령은 입니다 -d $'\0'. 이것은 작동하지만 -d ''작동하기 때문입니다! (이것은 명시적으로 문서화된 기능은 아니지만 read동일한 방식으로 작동한다고 가정합니다. ''문자열이 비어 있으므로 종료 null 바이트가 즉시 나타납니다. "첫 번째 문자"를 사용하는 것으로 문서화되어 있습니다.-d delimDelim", "첫 번째 문자"가 문자열 끝을 넘어서는 경우에도 작동할 것 같아요!)

하지만 당신의 예에서 find알 수 있듯이하나의 명령은 널 바이트를 인쇄할 수 있으며 해당 바이트는 이를 입력으로 읽는 다른 명령으로 파이프될 수 있습니다. 그 어떤 부분도 널 바이트 저장에 의존하지 않습니다.Bash의 문자열에서. 두 번째 예제의 유일한 문제점은 $'\0'명령의 인수에 이를 사용할 수 없다는 것입니다. echo "$file"$'\0'사용자가 원하는 경우 끝에 널 바이트가 인쇄됩니다.

echoprintf따라서 -style 문자열과 동일한 유형의 이스케이프 시퀀스를 지원하는 대신 사용할 수 있습니다 . $'...'이렇게 하면 문자열에 null 바이트를 포함하지 않고도 null 바이트를 인쇄할 수 있습니다. 다음과 같이 보일 것입니다:

for file in * ; do printf '%s\0' "$file" ; done \
  | while IFS= read -r -d '' ; do echo "$REPLY" ; done

아니면 그냥 다음과 같습니다:

printf '%s\0' * \
  | while IFS= read -r -d '' ; do echo "$REPLY" ; done

(참고: 실제로 널 바이트를 처리하고 인쇄하도록 하는 플래그가 echo있지만 파일 이름의 특수 시퀀스도 처리하려고 시도합니다. 따라서 이 방법이 더 강력합니다.)-e\0printf


그런데 껍질이 좀 있어요.하다문자열에는 Null 바이트가 허용됩니다. 예를 들어, 귀하의 예제는 Zsh에서 잘 작동합니다(기본 설정 가정). 그러나 쉘이 무엇이든 Unix 계열 운영 체제는 프로그램 인수 내에 널 바이트를 포함하는 방법을 제공하지 않으므로(프로그램 인수는 C 스타일 문자열로 전달되기 때문에) 항상 몇 가지 제한 사항이 있습니다. (귀하의 예제는 echo내장 셸이기 때문에 Zsh에서만 작동하므로 Zsh는 다른 프로그램 호출을 위한 OS 지원에 의존하지 않고 이를 호출할 수 있습니다. command echo대신 을 사용하면 echo내장된 셸을 우회하고 다음을 볼 수 있습니다. echo독립 실행형 프로그램을 사용하는 $PATHBash에서와 마찬가지로 Zsh에서도 동일한 동작이 발생합니다 .)

관련 정보