다음 내용이 포함된 파일에서 중복된 줄을 제거하고 싶습니다.시리아어스크립트. 소스 파일에는 3줄이 있는데, 첫 번째와 세 번째 줄은 동일합니다.
$ cat file.txt
ܐܒܘܢ
ܢܗܘܐ
ܐܒܘܢ
sort
and 를 사용하면 uniq
결과는 3개의 행이 모두 동일하다고 가정하는데 이는 잘못된 것입니다.
$ cat file.txt | sort | uniq -c
3 ܐܒܘܢ
로캘을 명시적으로 시리아어로 설정하는 것도 도움이 되지 않습니다.
$ LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c
3 ܐܒܘܢ
왜 이런 일이 발생합니까? 그게 중요하다면 저는 Kubuntu 18과 bash를 사용하고 있습니다.
답변1
uniq
-c
우분투 에서의 GNU 구현정확히 똑같다행이지만 순서가 동일한 연속 행의 수입니다.
GNU 시스템의 대부분의 국제 로케일에는 완전히 관련되지 않은 많은 문자가 동일한 정렬 순서로 정의되는 버그가 있습니다. 대부분은 정렬 순서가 전혀 정의되지 않았기 때문입니다. 대부분의 다른 운영 체제에서는 모든 문자의 정렬 순서가 다릅니다.
$ expr ܐ = ܒ
1
( expr
연산자 =
, 숫자가 아닌 인수의 경우 피연산자의 순서가 동일하면 1을 반환하고 그렇지 않으면 0을 반환합니다).
ar_SY.UTF-8
이는 또는 와 동일합니다 en_GB.UTF-8
.
필요한 것은 이러한 문자에 다른 정렬 순서가 부여된 로케일입니다. 우분투에 시리아 언어에 대한 로케일이 있는 경우 이러한 문자에 다른 정렬 순서가 부여될 것으로 예상할 수 있지만 우분투에는 그러한 로케일이 없습니다.
locale -a
지원되는 로케일 목록의 출력을 볼 수 있습니다 . dpkg-reconfigure locales
로 실행하여 더 많은 로케일을 활성화 할 수 있습니다 root
. localedef
의 정의 파일을 기반으로 더 많은 로케일을 수동으로 정의 할 수도 있지만 /usr/share/i18n/locales
시리아어에 대한 데이터는 찾을 수 없습니다.
다음 사항에 유의하세요.
LC_COLLATE=syr_SY.utf8 cat file.txt | sort | uniq -c
명령에 대해 LC_COLLATE 변수를 설정하기만 하면 됩니다. cat
이는 파일 내용을 출력하는 방법에 영향을 주지 않으며, cat
텍스트 유틸리티가 아니기 때문에 대조 또는 문자 인코딩에도 관심이 없습니다. sort
및 에 대해 모두 설정 하려고 합니다 uniq
. 또한 LC_CTYPE
UTF-8 문자 세트를 사용하여 로케일을 설정해야 합니다 .
시스템에 로케일이 없으므로 이는 syr_SY.utf8
로케일(기본 로케일)을 사용하는 것과 같습니다.C
실제로 여기의 C 로캘 또는 C.UTF-8이 아마도 사용하려는 로캘일 것입니다.
이러한 로케일에서 조합 순서는 코드 포인트, C.UTF-8의 유니코드 코드 포인트, C의 바이트 값을 기반으로 하지만 궁극적으로 해당 속성을 사용하는 UTF-8 문자 인코딩과 동일합니다.
$ LC_ALL=C expr ܐ = ܒ
0
$ LC_ALL=C.UTF-8 expr ܐ = ܒ
0
그래서:
(export LANG=ar_SY.UTF-8 LC_COLLATE=C.UTF-8 LANGUAGE=syr:ar:en
unset LC_ALL
sort <file | uniq -c)
문자 세트로 UTF-8이 포함된 LC_CTYPE, 코드 포인트 기반 정렬 순서 및 시리아어 또는 아랍어 오류 메시지(GNU coreutils sort
또는 uniq
메시지가 해당 언어로 번역된 경우)와 같은 로케일과 관련된 기타 설정이 있습니다. 아직 가지고 있지 않습니다).
이것들에 신경쓰지 않는다면다른설정 및 사용이 간편하며 휴대성이 뛰어납니다.
<file LC_ALL=C sort | LC_ALL=C uniq -c
또는
(export LC_ALL=C; <file sort | uniq -c)
@isaac이 이미 보여줬듯이.
1 POSIX 호환 uniq
구현은 로케일 정렬 알고리즘을 사용한 문자열 비교가 아니라 바이트 간 동등 비교를 의미합니다. 이는 2018년 버전의 표준에서 더욱 명확해졌습니다(참조:해당 오스틴 그룹 오류). 그러나 GNU는 uniq
현재 이를 사용하고 strcoll()
있으며 에도 대소문자를 구분하지 않는 비교 옵션이 POSIXLY_CORRECT
있습니다. -i
이는 아이러니하게도 로케일 정보를 사용하지 않고 ASCII 입력에서만 올바르게 작동합니다.
답변2
(간단한) 휴대용 솔루션:
$ ( LC_ALL=C sort syriac.txt | LC_ALL=C uniq -c )
2 ܐܒܘܢ
1 ܢܗܘܐ
시리아어 텍스트를 렌더링할 수 있는 글꼴이 없는 경우:
$ ( LC_ALL=C sort syriac.txt | LC_ALL=C uniq -c ) | xxd
00000000: 2020 2020 2020 3220 dc90 dc92 dc98 dca2 2 ........
00000010: 0a20 2020 2020 2031 20dc a2dc 97dc 98dc . 1 .......
00000020: 900a ..
편집하다
이는 실제 솔루션보다 해킹에 더 가깝습니다. 로케일 테이블에서 제공하는 데이터 정렬이 아닌 개별 바이트 값을 사용하여 각 행을 처리하는 방식으로 작동합니다 sort
. uniq
사용할 동등한 로케일(UTF-8 "코드 포인트 정렬 순서"가 "바이트 값 정렬 순서"와 동일한 순서이기 때문에)은 입니다 C.UTF-8
.
이는 대부분의 AFAICT 시스템에서 작동합니다.
동등한 솔루션은 다음과 같습니다.
$ ( export LC_COLLATE=C.UTF-8; <syriac.txt sort | uniq -c )
기본적인 문제는 시리아어(유니코드 코드 포인트)의 문자가U+0700–U+074F 시리아어그리고U+0860-U+086F 시리아어 보충자료)은 데이터 정렬 정렬 순서를 설정하지 않았습니다.
/usr/share/i18n/locales
이는 내부 로케일 정의 파일 (debian/ubuntu) 의 문제이며 less /usr/share/i18n/SUPPORTED
.
대개,로케일 이름은 일반적으로 "ll_CC" 형식을 취합니다. 여기서 "ll"은 ISO 639 2자리 언어 코드이고 "CC"는 ISO 3166 2자리 국가 코드입니다. 그리고시리아어(서부 변형) Syrj.
하지만시리아어에는 ISO 639-2에서 3자리 코드가 할당되었습니다.그리고639-2 코드의 공식 목록
이것국가 코드(ISO 3166)는 일반적으로 두 글자 코드입니다.아마도 SY 일 것입니다.ISO 3166 국가 코드 목록.
로케일과 관련된 하나 또는 모든 환경 변수를 설정하는 것만으로는 충분하지 않으며 모든 테이블이 손실되므로 (귀하의 경우처럼) 실패할 수 있습니다. 이 테이블에는 월 이름, 평일, 연도 공식, 시간 형식, 통화 형식, 오류 보고 언어(번역이 있는 경우) 등이 설정됩니다. 읽어주세요:로케일을 무엇으로 설정해야 하나요? 이것이 어떤 영향을 미칠까요?
유니코드 코드 포인트에 명시적으로 정의된 데이터 정렬이 없으면 정확히 동일하게 정의되지 않을 수 있습니다. 그것이 여기서 일어나고 있는 일입니다.
파일의 코드 포인트를 나열할 수 있습니다(예제 포인트 하나만 사용).
$ echo $(cat syriac.txt | grep -oP '\X' | sort)
ܐ ܒ ܘ ܢ ܢ ܗ ܘ ܐ ܐ ܒ ܘ ܢ
하지만 고유한 값만 얻으려고 하면 모든 값이 삭제됩니다.
$ echo $(cat syriac.txt | grep -oP '\X' | sort -u )
ܐ
이는 모든 문자가 동일한 조합 값(가중치)을 갖기 때문입니다.
$ a=ܐ
$ b=ܒ
$ [[ $a == [=$b=] ]] && echo yes
yes
이는 var 값이 a
var 값과 동일한 정렬 위치에 있음을 의미합니다.[=…=]
b
대신 반복되지 않는 문자가 나열됩니다.
$ echo $(cat syriac.txt | grep -oP '\X' | LC_COLLATE=C.UTF-8 sort -u )
ܐ ܒ ܗ ܘ ܢ
답변3
첫 번째 그룹 LC_CTYPE
:
$ export LC_CTYPE=syr_SY.utf8
$ <infile sort |uniq -c
2 ܐܒܘܢ
1 ܢܗܘܐ