$ '\ 0'이 (가) ''와 같은 이유는 무엇입니까?


10

몇 개의 파일로 작업을 수행하는 일반적인 방법은 다음과 같습니다.

for f in $(ls); do 

이제 공백이나 다른 이상한 문자가있는 파일로부터 안전하기 위해 순진한 방법은 다음과 같습니다.

find . -type f -print0 | while IFS= read -r -d '' file; 

여기서는에서와 -d ''같이 ASCII NUL을 설정하는 줄임말입니다 -d $'\0'.

그러나 왜 그렇습니까? 왜 ''$'\0'같은? 빈 문자열이있는 Bash의 C 루트로 인해 항상 null로 종료됩니까?


"순진한"방법을 참조하면 더 좋은 방법이 있습니까?
iruvar

2
그건 그렇고 for f in *파싱 ​​대신에 파일 세트를 반복적으로 안전하게 조작하려면 파싱 ​​대신 사용하십시오 ls.

내가 아는 @htor for i in $(ls)는 멍청한 짓이다. 나는 그것을 나쁜 예라고 여기는 부끄러운 일이다.
slhck

@ChandraRavoori 그렇습니다. 예를 들어 find … -exec파일을 반복 하는 대신 에 사용하는 것이 좋습니다. 이는 대신에 for 루프를 사용하는 대부분의 경우에 효과적입니다. 여기, find당신을 위해 모든 것을 돌봐.
slhck

@ slhck, 감사합니다. 가독성을 위해 루프를 선호 할 수있는 각 파일에 대해 다단계 작업과 관련된 상황은 어떻습니까? 위의 "순진한 방법"보다 더 나은 루프 옵션이 있습니까?
iruvar

답변:


10

man page of bash

          -d delim
                 The first character of delim is  used  to  terminate  the
                 input line, rather than newline.

문자열은 일반적으로 null로 끝나기 때문에 빈 문자열의 첫 번째 문자는 null 바이트입니다. -이해가 되네요 :)

소스는 다음과 같습니다.

static unsigned char delim;
[...]
    case 'd':
      delim = *list_optarg;
      break;

빈 문자열의 delim경우 단순히 널 바이트입니다.


"문자열은 일반적으로 null로 종료됩니다"라고 말할 때 POSIX 환경의 어딘가에 해당 하지 않습니까? 학교에서 C를 배우던 시절부터는 물론 그렇게하는 것이 합리적입니다. 방금 확인하고 있었어요
slhck

그러나 임의의 문자열을 임의로 많은 빈 문자열을 포함하는 것으로 간주 할 수 있습니다. 예를 들어 ''와 "X"를 연결하면 "X"가됩니다. 따라서 첫 번째 하위 문자열 bash가 발생하면 빈 문자열이라고 주장 할 수 있습니다. 예를 들어 자바 스크립트에서 빈 문자열을 사용하면 split()각 문자로 분할됩니다. "역사적 이유로"우리가 얻을 수있는 가장 좋은 설명 일 것으로 생각됩니다.
donothingsuccessfully

음, 확실히 "합치"때문에 C 스타일 '\0'와 함께 'X\0'당신을 제공한다 'X\0', 만약 완료 권리를. 이것은 자바 스크립트 @don 같은 언어에서 높은 수준의 기능을 할 일이 많이 없습니다
slhck

소스를 추가해 주셔서 감사합니다. delim = *list_optarg;왜 그런지 명확하게합니다.
slhck

@ slhck : 죄송합니다, 나는 자신을 명확하게하지 않았다. 당신은 "왜 질문 ''$'\0'michas는"그 코드가하는 일이다 "의 근접의 explaination을 준, 같은?". 나는 빈 문자열을 똑같이 합리적으로 보았을 때 다른 방법을 선택하는 것이 단순히 컨벤션이나 사건의 문제라고 제안했습니다.
donothingsuccessfully

6

bash에는 서로를 보상하는 두 가지 결함이 있습니다.

쓰면 $'\0'내부적으로 빈 문자열과 동일하게 취급됩니다. 예를 들면 다음과 같습니다.

$ a=$'\0'; echo ${#a}
0

그건 내부적으로 bash에 저장하기 때문에 모든 문자열 C에 있는 문자열, null로 끝나는 - 널 바이트 마크 문자열의 끝. Bash는 문자열을 문자열의 일부가 아닌 첫 번째 null 바이트로 자동으로 자릅니다.

# a=$'foo\0bar'; echo "$a"; echo ${#a}
foo
3

내장 -d옵션에 대한 인수로 문자열을 전달하면 readbash는 문자열의 첫 번째 바이트 만 봅니다. 그러나 실제로 문자열이 비어 있지 않은지 확인하지 않습니다. 내부적으로 빈 문자열은 널 바이트 만 포함하는 1 요소 바이트 배열로 표시됩니다. 따라서 bash는 문자열의 첫 번째 바이트를 읽는 대신이 널 바이트를 읽습니다.

그런 다음 내부적으로 read내장 뒤에있는 기계 는 널 바이트와 잘 작동합니다. 구분자를 찾을 때까지 바이트 단위로 계속 읽습니다.

다른 쉘은 다르게 동작합니다. 예를 들어 ash와 ksh는 입력을 읽을 때 널 바이트를 무시합니다. ksh를 사용하면 ksh -d ""개행까지 읽습니다. 쉘은 이진 데이터가 아니라 텍스트에 잘 맞도록 설계되었습니다. Zsh는 예외입니다. null 바이트를 포함하여 임의의 바이트를 처리하는 문자열 표현을 사용합니다. zsh에서 $'\0'길이가 1 인 문자열입니다 (그러나 read -d ''이상하게 동작합니다 read -d $'\0').


readbash 4.3에서 변경된 동작은 이제 null 바이트를 건너 뜁니다. 예를 들어 대신로 read x< <(printf a\\0a)설정 x합니다 . aaa
Lri
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.