새로운 질문의 경우이 스크립트는 다음과 같이 작동합니다.
#!/bin/bash
f() { for i in $(seq "$((RANDOM % 3 ))"); do
echo;
done; return $((RANDOM % 256));
}
exact_output(){ out=$( $1; ret=$?; echo x; exit "$ret" );
unset OldLC_ALL ; [ "${LC_ALL+set}" ] && OldLC_ALL=$LC_ALL
LC_ALL=C ; out=${out%x};
unset LC_ALL ; [ "${OldLC_ALL+set}" ] && LC_ALL=$OldLC_ALL
printf 'Output:%10q\nExit :%2s\n' "${out}" "$?"
}
exact_output f
echo Done
실행시 :
Output:$'\n\n\n'
Exit :25
Done
더 긴 설명
POSIX 쉘이 제거를 처리하는 일반적인 지혜 \n는 다음과 같습니다.
추가 x
s=$(printf "%s" "${1}x"); s=${s%?}
POSIX 사양 에 따라 명령 확장으로 마지막 줄 바꿈 ( S )이 제거 되므로 필요합니다 .
치환이 끝날 때 하나 이상의 문자 시퀀스를 제거합니다.
후행에 대해 x.
이 질문 x에서는 인코딩에서 일부 문자의 후행 바이트와 혼동 될 수 있다고합니다. 그러나 가능한 어떤 인코딩으로 어떤 언어에서 어떤 문자가 더 나은지 또는 어떤 문자가 더 좋을지 추측하는 방법은 무엇입니까?
하나; 그것은 단순히 틀렸다 .
우리가 따라야 할 유일한 규칙은 우리 가 제거하는 것을 정확하게 추가 하는 것입니다.
기존 문자열 (또는 바이트 시퀀스)에 무언가를 추가하고 나중에 정확히 동일한 것을 제거 하면 원래 문자열 (또는 바이트 시퀀스)이 같아야한다는 것을 쉽게 이해할 수 있습니다.
우리는 어디로 잘못 가나 요? 문자 와 바이트를 섞을 때 .
바이트를 추가하면 바이트를 제거해야하며, 문자를 추가 하면 정확히 동일한 문자를 제거해야합니다 .
두 번째 옵션 인 문자 추가 (나중에 정확히 동일한 문자 제거)는 복잡하고 복잡 할 수 있으며, 예를 들어 코드 페이지와 인코딩이 방해를받을 수 있습니다.
그러나 첫 번째 옵션은 가능하며 설명 후에는 단순 해집니다.
ASCII 바이트 (<127) 인 바이트를 추가하고 가능한 적은 복잡성을 유지하기 위해 az 범위의 ASCII 문자를 가정 해 보겠습니다. 또는 우리가 말했듯이 16 진수 범위의 바이트 0x61- 0x7a. x (실제로 byte 값 0x78) 중 하나를 선택할 수 있습니다 . x를 문자열에 연결하여 바이트를 추가 할 수 있습니다 (를 가정합니다 é).
$ a=é
$ b=${a}x
문자열을 바이트 시퀀스로 보면 다음과 같습니다.
$ printf '%s' "$b" | od -vAn -tx1c
c3 a9 78
303 251 x
x로 끝나는 문자열 시퀀스.
그 x (바이트 값 0x78)를 제거 하면 다음과 같은 결과가 나타납니다.
$ printf '%s' "${b%x}" | od -vAn -tx1c
c3 a9
303 251
문제없이 작동합니다.
좀 더 어려운 예입니다.
우리가 관심있는 문자열이 바이트로 끝났다고 가정 해 봅시다 0xc3.
$ a=$'\x61\x20\x74\x65\x73\x74\x20\x73\x74\x72\x69\x6e\x67\x20\xc3'
그리고 가치의 바이트를 추가하자 0xa9
$ b=$a$'\xa9'
문자열은 이제 다음과 같습니다.
$ echo "$b"
a test string é
정확히 내가 원하는 것, 마지막 두 바이트는 utf8에서 하나의 문자입니다 (따라서 누구나이 결과를 utf8 콘솔에서 재현 할 수 있습니다).
문자를 제거하면 원래 문자열이 변경됩니다. 그러나 그것은 우리가 추가 한 것이 아니라 바이트 값을 추가했습니다.이 값은 x로 쓰여지지만 어쨌든 바이트입니다.
바이트를 문자로 잘못 해석하는 것을 피하기 위해 필요한 것. 우리가 필요로하는 것은 우리가 사용한 바이트를 제거하는 액션이다 0xa9. 실제로 ash, bash, lksh 및 mksh는 모두 정확히 그렇게하는 것처럼 보입니다.
$ c=$'\xa9'
$ echo ${b%$c} | od -vAn -tx1c
61 20 74 65 73 74 20 73 74 72 69 6e 67 20 c3 0a
a t e s t s t r i n g 303 \n
그러나 ksh 또는 zsh는 아닙니다.
그러나 그것은 해결하기가 매우 쉽습니다. 모든 쉘에 바이트 제거를 지시하십시오.
$ LC_ALL=C; echo ${b%$c} | od -vAn -tx1c
그게 다야, 테스트 된 모든 쉘이 작동합니다 (yash 제외) (문자열의 마지막 부분).
ash : s t r i n g 303 \n
dash : s t r i n g 303 \n
zsh/sh : s t r i n g 303 \n
b203sh : s t r i n g 303 \n
b204sh : s t r i n g 303 \n
b205sh : s t r i n g 303 \n
b30sh : s t r i n g 303 \n
b32sh : s t r i n g 303 \n
b41sh : s t r i n g 303 \n
b42sh : s t r i n g 303 \n
b43sh : s t r i n g 303 \n
b44sh : s t r i n g 303 \n
lksh : s t r i n g 303 \n
mksh : s t r i n g 303 \n
ksh93 : s t r i n g 303 \n
attsh : s t r i n g 303 \n
zsh/ksh : s t r i n g 303 \n
zsh : s t r i n g 303 \n
그냥 간단, 모든 바이트 값을 정확히 한 바이트 인 LC_ALL = C 문자, 제거 쉘에게 0x00로가 0xff.
의견에 대한 해결책 :
주석에서 논의 된 예의 경우, 가능한 한 가지 솔루션 (zsh에서 실패)은 다음과 같습니다.
#!/bin/bash
LC_ALL=zh_HK.big5hkscs
a=$(printf '\210\170');
b=$(printf '\170');
unset OldLC_ALL ; [ "${LC_ALL+set}" ] && OldLC_ALL=$LC_ALL
LC_ALL=C ; a=${a%"$b"};
unset LC_ALL ; [ "${OldLC_ALL+set}" ] && LC_ALL=$OldLC_ALL
printf '%s' "$a" | od -vAn -c
인코딩 문제를 제거합니다.
$IFS이므로 인수로 캡처되지 않습니다.