이 파일의 모든 아포스트로피를 다음으로 변환하고 싶습니다 X
.
Bob's book
Bob’s book
Bob′s book # (Might look the same but actually different)
첫 번째 아포스트로피는 예상대로 대체됩니다.
$ cat file | tr "'" "X"
BobXs book
Bob’s book
Bob′s book
하지만 다른 두 종류의 아포스트로피를 사용하면 이상한 일이 발생합니다.
$ cat file | tr "’" "X"
Bob's book
BobXXXs book
BobXX�s book
$ cat file | tr "′" "X"
Bob's book
BobXX�s book
BobXXXs book
어떻게 작동하게 만들까요?
답변1
tr
바이트 단위로 작동합니다. 즉, UTF-8과 같은 멀티바이트 인코딩에서는 제대로 작동하지 않습니다. 내가 아는 유일한 해결책은 tr
유니코드를 지원하는 버전을 찾 거나 sed
문자열 교체를 수행할 수 있는 다른 도구로 전환하는 것입니다.
답변2
나에게 있어 tr은 OS가 UTF-8 코드 페이지를 사용하도록 구성되어 있는 한 ASCII와 UTF-8 파일을 모두 잘 처리합니다.
여기 내 예제 #1(Solaris 11)이 있습니다:
$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=
보시다시피 운영 체제는 utf-8을 사용하도록 구성되어 있습니다. utf-8 코드 페이지를 사용하여 다음 두 파일을 만들었습니다.
$ cat file
Bob’s Bob′s Bob's
$ cat apos
’′'
그런 다음 다음과 같이 모든 apos를 교체할 것으로 예상되는 결과를 얻었습니다.
$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs
내 예제 #2(Solaris 10)는 다음과 같습니다.
$ locale
LANG=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=
여기에서 운영 체제가 utf-8이 아닌 단순 ASCII를 처리하도록 구성되어 있으므로 tr을 사용하여 멀티바이트 문자가 있는 utf-8 파일을 처리하는 데 문제가 있을 수 있음을 알 수 있습니다. 그러나 해결책이 있습니다. 긴 tr 명령은 입력 문자의 8진수 표현을 허용하므로 지정된 문자의 모든 바이트를 8진수 표현으로 바꿀 수 있습니다.
귀하의 경우에는 다음이 있습니다.
char hex octal
’ E2 80 99 \342\200\231
′ E2 80 B2 \342\200\262
' 27 \47
첫 번째와 두 번째 apos는 3바이트로 표시됩니다. 세 번째는 표준 ASCII(1바이트)입니다.
따라서 첫 번째 위치를 바꾸려면 다음을 사용할 수 있습니다.
$ cat file | tr "\342\200\231" "\0\0x"
Bobxs Bob▒s Bob's
두번째:
$ cat file | tr "\342\200\262" "\0\0x"
Bob▒s Bobxs Bob's
제삼:
$ cat file | tr "\47" "x"
Bob’s Bob′s Bobxs
한 번에 모든 것을 교체하려면 다음을 사용할 수 있습니다.
$ cat file | tr "\342\200\231\262\47" "\0\0xxx"
Bobxs Bobxs Bobxs
물론 완벽하지는 않습니다. 이렇게 하면 파일에서 \342, \200, \231, \262 바이트의 모든 항목이 대체되므로 이러한 바이트를 포함하는 다른 멀티바이트 문자가 손상됩니다. 하지만 파일에 다른 멀티바이트 문자가 포함되어 있지 않으면 작동합니다.