다음과 같은 문자열이 있습니다.
one one tow tow three three tow one three
중복된 단어를 제거하고 다음과 같이 만들려면 어떻게 해야 합니까?
one tow three tow one three
요점은 인접한 중복 단어만 제거하는 스크립트를 작성하고 싶다는 것입니다
나는 시도했다:
echo "$string" | awk '{for (i=1;i<=NF;i++) if (!a[$i]++) printf("%s%s",$i,FS)}{printf("\n")}'
그러나 인접하지 않은 중복 단어도 제거합니다.
답변1
다중 문자 RS
및 \s
속기용으로 GNU awk 사용:
$ echo 'one one tow tow three three tow one three' |
awk -v RS='\\s+' '
$0 != prev { out = (NR>1 ? out OFS : "") $0; prev = $0 }
END { print out }
'
one tow three tow one three
또는 여전히 GNU awk이지만 다음에서 영감을 받았습니다.@nezabudka의 답변그러나 입력 필드를 구분하는 공백 순서와 입력 필드에 포함된 문자에 관계없이 올바르게 작동하고 출력이 이와 같이 끝나도록 일부 수정이 이루어졌으므로 \n
유효한 POSIX 텍스트입니다. 문서:
$ echo one one tow tow three three tow one three |
awk -v RS='[[:blank:]]+' '
$1 != prev { out = out $1 RT; prev=$1 }
END { print out }
'
one tow three tow one three
그렇지 않으면 awk를 사용하십시오.
$ echo 'one one tow tow three three tow one three' |
awk '{
out = $1
for ( i=2; i<=NF; i++ ) {
if ( $i != $(i-1) ) {
out = out OFS $i
}
}
print out
}'
one tow three tow one three
답변2
행에 2500개(예: 1000개) 이하의 열이 있는 경우:
echo one one tow tow three three tow one three |
fmt -1 | uniq | fmt -1000
GNU awk:
echo one one tow tow three three tow one three |
awk -v RS=' ' '$1 != D {printf "%s", $1 (RT?RS:ORS); D=$1}'
업데이트(줄 바꿈으로 끝나는 것이 확실한 경우):
echo one one tow tow three three tow one three |
awk -v RS='[[:space:]]' '$1 != D {printf "%s", $1 RT; D=$1}'
그렇지 않은 경우(일반적인 방법):
echo -n one one tow tow three three tow one three |
awk -v RS='[[:space:]]' '$1 != D {printf "%s", $1 (RT?RT:ORS); D=$1}'
참고:
GNU 버전에는 RS의 템플릿에 해당하는 실제 값이 할당되는 내장 변수 RT가 있습니다. 예를 들어, [[:space:]]
RS 변수에 대해 템플릿이 정의된 경우 RT 변수에는 각 경우(공백, 탭 또는 개행)에서 레코드를 종료하는 문자가 동적으로 할당됩니다. RS 변수에 문자 클래스 템플릿이 할당된 경우 RS=[[:space:]]
삼항 연산자를 (RT?RT:ORS)
또는 로 변경해야 합니다.RT
답변3
uniq
다른 줄 뒤에 모든 단어를 넣으려면 다음을 사용할 수 있습니다.
string='one one tow tow three three tow one three'
printf '%s\n' "${string// /
}" | uniq | paste -sd ' ' -
또는 를 사용하여 perl
여러 공백 문자를 허용하여 단어를 구분하고 반복 그룹 사이의 공백을 유지합니다.
string=' one one tow tow three three tow one three '
perl -le 'print s/(?<!\S)(\S+)(\s+\1)+(?!\S)/\1/gr for @ARGV' -- "$string"
다음을 제공합니다:
one tow three tow one three
ksh93의 ${var//pattern/replacement}
매개변수 확장 연산자와 동일합니다(bash를 포함한 일부 다른 쉘은 이 연산자를 복사했지만 고급 패턴 일치 연산자는 복사하지 않았습니다).
$ string=' one one tow tow three three tow one three '
$ print -r - "${string//~(<!\S)+(\S)+(+(\s)\1)~(!\S)/\1}"
one tow three tow one three
또는 zsh
(다른 쉘은 Perl과 유사한 패턴 일치 연산자를 지원함)을 사용하여 해당 변수를 수정합니다.
$ string=' one one tow tow three three tow one three '
$ autoload regexp-replace
$ set -o rematchpcre
$ regexp-replace string '(?<!\S)(\S+)(\s+\1)+(?!\S)' '$match[1]'
$ print -r - "$string"
one tow three tow one three
또는 fish
:
$ set string ' one one tow tow three three tow one three '
$ string replace -a --regex '(?<!\S)(\S+)(\s+\1)+(?!\S)' '$1' $string
one tow three tow one three
예제의 단어가 모두 숫자(또는 밑줄)로 구성된 경우 비지박스 구현과 유사한 접근 방식을 취할 수 있습니다. awk
여기서 음수 탐색 perl 연산자는 \<
및 \>
단어 경계 연산자로 대체될 수 있습니다(perl의 와 유사 하므로 / \b
에 더 가깝습니다 ). Perl 둘러보기 연산자로):(?<!\w)
(?!\w)
$ printf '%s\n' "$string" | busybox awk '{print gensub("\\<(\\S+)(\\s+\\1)+\\>", "\\1", "g")}'
one tow three tow one three
단어에 숫자나 밑줄 이외의 문자가 포함되어 있으면 이 방법을 사용할 수 없습니다. 예를 들어 와 와 사이에 단어 경계가 있기 one-two two three
때문에 로 변경됩니다.one-two three
-
two
답변4
펄을 사용하세요. 예를 들어, 다음은 줄 경계를 넘어 인접한 중복 단어를 제거할 수도 있습니다(perl의 -0777
옵션을 사용하여 전체 입력을 한 번에 흡수함).
$ printf 'one one two\n two two\ntwo three three two\none\nthree\nthree\n' |
perl -0777 -p -e 's/\b(\w+)(?:\s+\1)+\b/$1/g'
one two three two
one
three
\1
작업의 왼쪽(LHS)은 s/search (LHS)/replace (RHS)/
이전에 일치된 패턴 그룹에 대한 역참조 입니다 (\w+)
. $1
교체 작업 또는 작업 오른쪽에 있는 동일한 캡처 그룹입니다.
그런데 이것을 Perl에 입력하지 않으면 입력은 다음과 같이 보입니다. 여러 줄에는 반복되는 인접 단어가 포함되어 있습니다.
$ printf 'one one two\n two two\ntwo three three two\none\nthree\nthree\n'
one one two
two two
two three three two
one
three
three
노트:
\b
^
or 와 유사한 앵커이지만$
줄의 시작이나 끝을 일치시키는 대신 단어 사이의 (너비가 0인) 경계와 일치합니다.\w
매뉴얼 페이지에 다음과 같이 정의된 모든 단어 문자와 일치합니다perlre
.
\w [3]은 "단어" 문자와 일치합니다(영숫자와 "_", 기타 연결 구두점 및 유니코드 마커).
...
[3] 자세한 내용은 perlunicode의 "유니코드 문자 속성"을 참조하세요.
알파벳(예: 알파벳) 문자(숫자나 밑줄 제외)만 엄격하게 일치시키려는 경우 [[:alpha:]]+
대신 를 사용할 수 있습니다 \w+
.
- 입력 텍스트에 유니코드 문자가 포함된 경우 이를 처리하는 방법에는 여러 가지가 있지만 가장 간단한 방법은 Perl의
-C
옵션을 사용하는 것입니다.
$ echo 'öne öne öne two öne one' |
perl -C -0777 -p -e 's/\b([[:alpha:]]+)(?:\s+\1)+\b/$1/g'
öne two öne one
이 옵션에 대한 세부정보를 보고 man perlrun
검색하세요 . -C
이 주제에 정말로 관심이 있다면 , perlunicode
및 perlunitut
에 perluniintro
대한 매뉴얼 페이지도 참조하십시오 perlunifaq
. 광범위한 문서에서 짐작할 수 있듯이 유니코드를 다루는 것은 대부분의 경우 간단하고 간단하지만 다양한 상황에서는 매우 복잡하고 미묘할 수 있습니다.