특정 열(예에서는 $2)에서 중복 필드(쉼표로 구분)를 제거하고 싶습니다.
입력 파일:
A 1,2,3,4
B 4,5,6,3
C 2,15
예상 출력:
A 1,2,3,4
B 5,6
C 15
답변1
perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e'
위 코드를 다음과 같이 실행할 수 있습니다.
$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile
A 1,2,3,4
B 5,6
C 15
어떻게 작동하나요?
를 처음 호출 perl
하면 -lpe
다음 세 가지 작업이 수행됩니다.
-l[octal]
줄 끝 처리를 활성화하고 줄 종결자를 지정합니다.-p
-n과 같은 반복을 가정하고 sed와 같은 라인 인쇄도 가정합니다.-e program
한 줄 프로그램(여러 -e 허용, 프로그램 파일 생략)
이는 본질적으로 파일을 가져와 개행 문자를 제거하고 한 줄에서 작동한 다음 작업이 완료되면 다시 개행 문자를 추가합니다. 따라서 파일을 반복하면서 각 파일에 대해 Perl 코드를 차례로 실행합니다.
실제 Perl 코드는 다음과 같습니다.
\s
공백 문자( 예: 최신 버전에서는[ \f\n\r\t]
5 자 )를 나타냅니다.\v
perl
[[:space:]]
\K
내용을 \K 왼쪽에 유지하고 $&에 포함하지 마세요.\S+
세트에 없는 하나 이상의 문자 [\f\n\r\t\v]
결과가 수집 되고 join ",",
각 필드가 쉼표로 구분되도록 다시 결합됩니다.
는 split ",", $&
에서 찾은 일치 항목을 가져와서 \S+
쉼표 없이 필드로만 분할합니다.
각 필드의 번호를 가져 와서 grep {!$seen{$_}++}
해시에 추가합니다. $seen{}
여기서 각 필드의 번호는 $_
각 필드를 반복할 때의 번호입니다. 필드 번호가 "표시"될 때마다 ++
연산자를 통해 계산 됩니다 $seen{$_}++
.
grep{!$seen{$_}++}
필드 값이 한 번만 발생하면 필드 값이 반환됩니다.
수정하고 무슨 일이 일어나는지 확인하세요
이 수정된 혐오스러운 기능을 사용하면 Perl 코드의 한 줄이 파일에서 여러 줄로 이동할 때 어떤 일이 발생하는지 확인할 수 있습니다.
$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile
keys: 4 1 3 2 | vals: 1 1 1 1
A 1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B 5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C 15
$seen{}
이는 처리된 파일의 줄 끝에 무엇이 있는지 보여줍니다. 파일의 두 번째 줄을 살펴보겠습니다.
B 4,5,6,3
다음은 다음과 같이 줄을 표시하는 수정된 버전입니다.
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
즉, 필드 #6(1회), 필드 #4(2회) 등, 필드 #5(1회)를 확인했다는 의미입니다. 따라서 grep{...}
결과가 반환될 때 해당 배열의 결과가 이 행(4,5,6,3)에 나타나고 1회(6,1,15,5)만 표시되는 경우 해당 배열의 결과만 반환됩니다. 이 두 목록의 교집합은 (5,6)이고 이것이 반환됩니다 grep
.
인용하다
답변2
{
tr -cs '0-9ABC' '[\n*]' |
nl -w1 -s: |
sort -t: -uk2,2 | sort -t: -k1,1n |
sed 's/[^:]*://;/^[ABC]/!{H;$!d
};x;y/\n/,/;s/,/\t/'
} <<\FILE
A 1,2,3,4
B 4,5,6,3
C 2,15
FILE
기본적으로 데이터가 실행되고 sort -u
전송되었을 때와 상대적으로 동일한 형태로 반환되는지 확인하기 위해 몇 가지 단계를 수행하는 것입니다.
First는 tr
숫자가 아닌 입력의 모든 바이트를 ABC
줄 바꿈으로 변환하고 모든 반복을 압축합니다. 따라서 각 필드에는 자체 행이 있습니다.
nl
그런 다음 입력의 각 행에 번호를 매깁니다. 출력은 다음과 같습니다.
1:A
2:1
3:2
...등.
다음으로 데이터가 sort
두 번 편집됩니다. 첫 번째는 sort
모든 중복 필드를 제거합니다. 각 행의 두 번째 데이터 필드에서만 작동하고 이를 로 바꿉니다 . 두 번째는 :
이번에 첫 번째 필드( 번호가 매겨진 시퀀스) 를 지정하여 sort
원래 순서를 복원합니다 .nl
sort
마지막으로 sed
모든 것을 다시 합치십시오. 입력을 수집하고, 모든 nl
삽입을 제거하고, x
행 ABC
과 $
마지막 행의 보류 및 모드 버퍼를 변경합니다. 각 줄에서 y
모든 ewline을 쉼표 \n
로 변환하고 각 줄의 첫 번째 줄을 마지막 a로 바꿉니다.,
<tab>
결과는 어떻습니까?
산출
A 1,2,3,4
B 5,6
C 15
친구 여러분, 이것이 바로 유닉스의 아름다움입니다.
그리고 궁금하기 때문에 sed
개별 항목은 다음과 같습니다.
sed 's/\t\(.*\)/,\1,/;H;$!d;x;:t
s/,\([^,]*,\)\(.*,\)\1/,\1\2/;tt
s/,\(\n.\),/\1\t/g;s/,\(.*\),/\t\1/'
' <<\FILE
A 1,2,3,4
B 4,5,6,3
C 2,15
FILE
첫 번째와 세 번째 줄은 준비/정리와 같습니다. 첫 번째는 전체 파일을 패턴 공간으로 가져오고 각 필드가 쉼표로 구분되어 있는지 확인합니다. 예를 들어 첫 번째 줄은 다음과 같습니다.
A,1,2,3,4,
지나고 나면 이런 모습입니다.
두 번째 행은 모든 작업이 완료되는 곳입니다. 이는 실제로 재귀 교체 기능입니다. 더 이상 필드를 찾을 수 없을 때까지 다른 곳에서만 일치할 수 있는 쉼표로 구분된 필드를 계속 교체합니다. 이것이 est 명령이 수행하는 작업입니다 t
.
내가 말했듯이 세 번째 줄은 모든 것을 정리합니다. 쉼표와 탭 등을 원래 위치로 되돌리고 최종 결과는 다음과 같습니다.
산출
A 1,2,3,4
B 5,6
C 15
재미있지만, 상당한 양의 데이터에 대해 이 작업을 수행하는 것은 컴퓨터 살인입니다. 이는 재귀 정규 표현식으로, 아마도 최악의 재귀 유형일 수 있습니다. 따라서 단 몇 줄의 코드만으로 이런 종류의 작업을 수행하는 것이 가능하지만 이런 식으로 스프레드시트를 만드는 것은 아마도 현명하지 못할 것입니다.
답변3
그걸로 awk
사용하기 쉽습니다array
,split
, 뿐만 아니라 정규loop
:
{
split($2, elements, ",")
out = ""
for (i in elements) {
el = elements[i]
if (!(el in used)) {
out = out el ","
}
used[el] = 1
}
sub(/,$/, "", out)
$2 = out
}
1
각 행에 대해 두 번째 열을 쉼표로 구분하고 비트를 배열에 저장합니다 elements
. 그런 다음 루프를 사용하여 해당 열에 대한 새 값을 작성하고 이전에 해당 값을 본 적이 있는지 확인합니다. (연관) 배열에서 이미 본 값 세트를 유지합니다 used
. 이전에 본 적이 있는 경우 el in used
출력에 넣지 않아야 합니다. 그렇지 않으면 새로운 것이므로 out
다시 사용하지 않도록 연결하여 본 값 집합에 추가합니다. 마지막으로 구성된 목록을 다시 두 번째 열에 넣습니다. 이는 본질적으로 다른 언어에서 취하는 접근 방식입니다.
위 코드를 파일에 넣고 awk -f
또는 작은따옴표를 사용하여 명령줄에서 인수로 모두 실행합니다.