28개 필드/헤더/속성(쉼표로 구분)이 포함된 파일이 있습니다. 필드 #은 레코드를 고유하게 만듭니다. 그러나 나머지 필드는 동일할 수 있습니다. 중복된 항목을 찾아 하나만 보관해야 합니다. 두 번째 반복보다 첫 번째 반복을 유지하는 것이 더 쉽다면 괜찮습니다. 예:
입력 파일:
1,ed23,jon,doe,director,usa
2,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
원하는 출력:
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
답변1
예제 입력은 혼란스럽습니다. 첫 번째 행(열 머리글)에는 필드 구분 기호 쉼표도 없고 대부분의 행에는 성과 성적 필드 사이에 쉼표가 없습니다.
약간 건전한 입력을 제공하기 위해 다음과 같이 편집했습니다.
$ cat input.txt
ID, uid ,firstname ,lastname, grade , country n28
1 , ed23 , jon , doe , director , usa
2 , ed23 , jon , doe , director , usa
3 , er67 , jake , Kogan , director , usa
4 , er67 , jake , Kogan , director , usa
5 , dc10 , Charls ,Morg , manager , usa
6 , kc56 , patel ,Kumar , associate , india
치트를 제거하는 간단한 구현은 다음과 같습니다.
$ awk -F' *, *' -v OFS=, \
'NR==1 {$1=$1;$0=$0; print; next};
{id=$1; $1=""; $0=$0; if (!seen[$0]++) {print id $0}}' input.txt
ID,uid,firstname,lastname,grade,country n28
1,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
이는 입력 필드 구분 기호( FS
)를 0개 이상의 공백으로 설정하고 그 뒤에 쉼표, 0개 이상의 공백을 설정하고 OFS
출력 필드 구분 기호( )를 쉼표로만 설정합니다. 즉, 모든 필드에서 선행 및 후행 공백을 효과적으로 제거합니다.
첫 번째 입력 라인( NR==1
)의 경우 awk 트릭을 사용하여 입력 라인의 형식을 다시 지정합니다. 즉, 필드를 변경하고(심지어 원래 값으로 설정) 을 설정합니다 $0=$0
. 라인은 새로운 OFS를 사용하도록 재형식화됩니다. 그런 다음 인쇄하고 다음 줄로 이동합니다.
나머지 입력의 경우 $1이라는 변수에 $1을 id
빈 문자열로 설정한 다음 $0=$0
ID와 줄의 나머지 부분을 인쇄하기 전에 트릭을 다시 사용합니다(줄에서 $1을 효과적으로 제거).
예제 출력과 달리 다음은 인쇄됩니다.첫 번째마지막 줄이 아닌 중복된 줄 - 처음 본 때는 감지하기 쉽지만 마지막으로 본 때는 감지하기 어렵습니다(입력 내용을 모두 읽지 않으면 알 수 없음). 또한 이는 반복 횟수를 계산하지 않습니다.
이 두 가지를 모두 수행하려면 출력을 생성하기 전에 전체 입력 파일을 읽고 두 번째 배열 ids
( 중요한 라인일 수도 있습니다.
$ awk -F' *, *' -v OFS=, \
'NR==1 {$1=$1;$0=$0",count";print;next};
{id=$1; $1=""; $0=$0; seen[$0]++; ids[$0]=id};
END { for (id in ids) {print ids[id] id, seen[id]} }' input.txt | \
sort -n
ID,uid,firstname,lastname,grade,country n28,count
2,ed23,jon,doe,director,usa,2
4,er67,jake,Kogan,director,usa,2
5,dc10,Charls,Morg,manager,usa,1
6,kc56,patel,Kumar,associate,india,1
sort -n
이는 awk의 연관 배열이 순서가 없으므로 반 무작위 순서로 나타나기 때문에 사용됩니다. GNU awk에는 여기서 배열에 사용할 asort()
수 있는 값으로 배열을 정렬할 수 있는 함수가 있지만 ids
a) 이식성이 없고 b) 출력을 sort -n
.
답변2
쉼표로 구분된 깔끔한 입력의 경우 awk
다음과 같은 스크립트가 적합할 수 있습니다.
awk -F, '{X=""; for (i=2;i<29;i++) X=X " " $i;} \
seen[X]!=1 {print;} \
{seen[X]=1;}' < input
첫 번째 awk
규칙은 입력에서 2~28개의 "단어"를 선택하여 "키"를 만듭니다(인수에 따라 -F,
쉼표로 구분된 모든 항목은 "단어"임). 다음 규칙은 "키"가 이미 등록되지 않은 한 해당 줄을 인쇄하고 세 번째 규칙은 해당 줄에 대한 키를 등록합니다.
답변3
파일이 "간단한 CSV" 형식이라고 가정합니다. 즉, 데이터에 쉼표나 줄 바꿈이 포함되어 있지 않다는 의미입니다.
$ tac file | awk -F , '{ key = $0; sub("[^,]*,", "", key) } !seen[key]++' | tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
awk
위 파이프라인 중간에 있는 코드는 첫 번째 행을 제외한 모든 필드에 대한 해시의 키로 사용될 문자열을 생성합니다. 그것은 인쇄됩니다첫 번째특정 키가 있는 행이 나타나고 모든 중복 항목은 무시됩니다.
당신이 얻고 싶어하는 것 같으니마지막tac
반복하려면 (GNU coreutils에서)을 사용하여 프로그램에 입력하기 전에 입력 줄의 순서를 반대로 바꿉니다 awk
. 그런 다음 프로그램의 출력을 반전시킵니다 awk
.
이 접근 방식의 단점은 계산된 키가 모든 고유 행에서 첫 번째 필드를 뺀 크기를 합친 만큼의 메모리를 사용한다는 것입니다.
다음은 보다 메모리 효율적인 접근 방식이지만 중복 행이 항상 함께 표시되도록 입력이 정렬되어 있다고 가정합니다.
$ tac file | awk -F , '{ key = $0; sub("[^,]*,", "", key) } key != prev; { prev = key }' | tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
답변4
uniq
위의 설명을 자세히 설명하려면 - 방법:
$ tr ',' '\t' < temp/testfile | uniq -f 1 | tr '\t' ','
1,ed23,jon,doe,director,usa
3,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india
\t
데이터에서 공백을 피하기 위한 구분 기호 로 사용됩니다 .
uniq
발견된 첫 번째 고유 행이 유지됩니다. "마지막" 항목을 반드시 유지해야 하는 경우 파일 끝부터 처음까지 작업해야 합니다. 다음 방법을 사용하여 이 작업을 수행할 수 있습니다 tac
.
$ tac temp/testfile|tr ',' '\t' | uniq -f 1 | tr '\t' ','|tac
2,ed23,jon,doe,director,usa
4,er67,jake,Kogan,director,usa
5,dc10,Charls,Morg,manager,usa
6,kc56,patel,Kumar,associate,india