두 파일에서 공통적 인 행을 찾는 유닉스 명령


179

한 번 이상 두 개 이상의 파일에서 공통 줄을 인쇄 할 수있는 유닉스 명령을 찾았습니다. 누가 그 이름을 알고 있습니까? 보다 훨씬 간단했다 diff.


5
comm정렬 된 입력 파일이 필요하므로이 질문에 대한 답변이 모든 사람이 원하는 것은 아닙니다 . 라인 단위 공통을 원한다면 훌륭합니다. 그러나 당신이 "anti-diff"라고 부르는 것을 원한다면 comm그 일을하지 마십시오.
Robert P. Goldman

@ RobertP.Goldman은 file1에 부분 패턴이 pr-123-xy-45있고 file2에 포함되는 경우 두 파일간에 공통되는 방법이 있습니다 ec11_orop_pr-123-xy-45.gz. 다음을 포함하는 file3이 필요합니다ec11_orop_pr-123-xy-45.gz
Chandan Choudhury

이 참조 줄 단위로 텍스트 파일을 정렬
Y2K-하기 Shubham을

답변:


216

당신이 찾고있는 명령은 comm입니다. 예 :-

comm -12 1.sorted.txt 2.sorted.txt

여기:

-1 : 열 1을 숨 깁니다 (1.sorted.txt에 고유 한 행)

-2 : 열 2 억제 (2.sorted.txt에 고유 한 행)


27
일반적인 사용법 : comm -12 1.sorted.txt 2.sorted.txt
Fedir RYKHTIK

45
comm은 정렬 된 파일을 필요로하지만 grep -f file1 file2를 사용하여 두 파일의 공통 행을 얻을 수 있습니다.
ferdy

2
@ ferdy (당신의 답변에서 내 의견을 반복하는 것은 본질적으로 의견으로 게시 된 반복 답변이므로) grep예상치 못한 이상한 일을합니다. 구체적으로 모든 내용은 1.txt일반 문자열이 아닌 정규식으로 해석됩니다. 또한 빈 줄은의 1.txt모든 줄과 일치합니다 2.txt. 그래서 grep매우 구체적인 상황에서 작동합니다. 적어도 fgrep(또는 grep -f) 를 사용하고 싶지만 빈 줄이 아마도이 과정을 혼란스럽게 할 것입니다.
Christopher Schultz

11
아래의 ferdy답변Christopher Schultz 및 내 의견을 참조하십시오. TL; DR - 사용 grep -F -x -f file1 file2.
Jonathan Leffler

1
@bapors : 명령 의 출력 comm을 3 개의 별도 파일로 가져 오는 방법으로 자체 답변 Q & A를 제공 했습니까? 대답이 너무 커서 여기에 편안하게 들어갈 수 없었습니다.
Jonathan Leffler

62

정렬되지 않은 파일에 comm 명령 을 쉽게 적용하려면 Bash의 프로세스 대체를 사용하십시오 .

$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321

따라서 abc 및 def 파일에는 공통으로 한 줄이 있고 "132"줄이 있습니다. 정렬되지 않은 파일에서 통신 사용 :

$ comm abc def
123
    132
567
132
    777
    321
$ comm -12 abc def # No output! The common line is not found
$

마지막 라인은 출력을 생성하지 않았으며 공통 라인은 발견되지 않았습니다.

이제 정렬 된 파일에 comm 을 사용 하여 파일을 프로세스 대체로 정렬하십시오.

$ comm <( sort abc ) <( sort def )
123
            132
    321
567
    777
$ comm -12 <( sort abc ) <( sort def )
132

이제 우리는 132 라인을 얻었습니다!


2
그래서 ... sort abc > abc.sorted, sort dev > def.sorted다음 comm -12 abc.sorted def.sorted?
Nikana Reklawyks

1
@NikanaReklawyks 그런 다음 나중에 임시 파일을 제거하고 오류가 발생할 경우 정리에 대처하십시오. 많은 시나리오에서 결과가 메모리에 맞는 한 디스크 I / O를 피할 수 있기 때문에 프로세스 대체가 훨씬 빠릅니다.
tripleee

29

Perl 원 라이너를 보완하기 위해 다음 awk과 같습니다.

awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2

그러면 모든 줄을 file1배열로 읽어 들인 arr[]다음 각 줄 file2이 이미 배열 내에 있는지 확인합니다 (예 :) file1. 찾은 줄은에 나타난 순서대로 인쇄됩니다 file2. 비교 in arrfile2색인에서 배열 까지의 전체 행을 사용하므로 전체 행에서 정확히 일치하는 항목 만보고합니다.


2
THIS (!)가 정답입니다. 다른 것들은 일반적으로 작동하도록 만들 수 없습니다 (나는 시도하지 perl않았기 때문에). 고마워요, Ms.
entonio

1
공통 회선을 표시 할 때 순서를 유지하는 것은 통신 때문에 제외 된 일부 경우에 실제로 유용 할 수 있습니다.
tuxayo

1
누군가가 특정 열을 기반으로 동일한 작업을 수행하려고하지만 awk을 모르는 경우 $ 5를 열 5의 경우 $ 5로 바꾸면 5 열의 동일한 단어가있는 2 개의 파일에서 공유되는 행을 얻습니다.
FatihSarigol

24

아마 당신은 의미 comm합니까?

정렬 된 파일 FILE1과 FILE2를 한 줄씩 비교하십시오.

옵션이 없으면 3 열 출력을 생성합니다. 열 1에는 FILE1에 고유 한 행이 있고 열 2에는 FILE2에 고유 한 행이 있고 열 3에는 두 파일에 공통 인 행이 있습니다.

이러한 정보를 찾는 비결은 정보 페이지입니다. GNU 프로그램의 경우, 맨 페이지보다 훨씬 상세합니다. 시도해 info coreutils보면 작은 유용한 유틸리티가 모두 나열됩니다.


19

동안

grep -v -f 1.txt 2.txt > 3.txt

두 파일의 차이점을 제공합니다 (1.txt가 아닌 2.txt에 있음).

grep -f 1.txt 2.txt > 3.txt

모든 공통 라인을 수집하여 문제점에 대한 쉬운 솔루션을 제공해야합니다. 파일을 정렬 한 경우에도 가져 가야 comm합니다. 문안 인사!


2
grep예상치 못한 이상한 일을합니다. 구체적으로 모든 내용은 1.txt일반 문자열이 아닌 정규식으로 해석됩니다. 또한 빈 줄은의 1.txt모든 줄과 일치합니다 2.txt. 따라서 이것은 매우 구체적인 상황에서만 작동합니다.
Christopher Schultz

13
@ChristopherSchultz : POSIX grep표기법을 사용하여 더 잘 작동하도록이 답변을 업그레이드 할 수 있습니다. POSIX 표기법 grep은 대부분의 최신 Unix 변형에서 지원됩니다 . 정규식을 억제 -F하려면을 추가 하거나 사용 하십시오 fgrep. -x전체 줄에만 일치하도록 (정확히) 추가하십시오 .
Jonathan Leffler

comm정렬 된 파일을 사용해야하는 이유는 무엇 입니까?
Ulysse BN

2
@UlysseBN comm은 메모리에 3 줄만 있으면되기 때문에 정렬 된 한 임의로 큰 파일을 사용할 수 있습니다 (GNU comm가 줄이 실제로 길면 접두사를 유지하는 것으로 알고 있습니다). grep솔루션은 메모리에 모든 검색 식을 유지해야합니다.
tripleee

9

두 파일이 아직 정렬되지 않은 경우 다음을 사용할 수 있습니다.

comm -12 <(sort a.txt) <(sort b.txt)

comm: file 2 is not in sorted order 할 때 오류 메시지를 피하면서 작동 comm -12 a.txt b.txt합니다.


당신 말이 맞지만, 이것은 본질적으로 다른 대답을 반복 하며 실제로 아무런 이점을 제공하지 않습니다. 잘 정립되고 정답이있는 오래된 질문에 대답하기로 결정한 경우, 늦은 시간에 새 답변을 추가하면 크레딧을 얻지 못할 수 있습니다. 독특한 새로운 정보가 있거나 다른 답변이 모두 잘못되었다고 확신하는 경우 반드시 새로운 답변을 추가하되, 질문이 일반적으로 제기 된 후 오랫동안 동일한 기본 정보를 제공하는 '아직 다른 답변'을 작성하십시오. ' 당신은 많은 신용을 얻습니다.
Jonathan Leffler

나는이 부분이 @JonathanLeffler라는 대답을 보지 못했습니다. 다른 대답은 더 정확하지만 빠른 해결책을 원하는 사람에게는 읽을 줄이 2 줄 밖에 없다는 것이 나의 이점입니다. 때때로 우리는 자세한 답변을 찾고 때로는 서두르고 빠르게 읽을 수있는 붙여 넣기 답변이 좋습니다.
Basj

또한 신용 / 담당자에 대해서는 신경 쓰지 않으며이 목적으로 게시하지 않았습니다.
Basj

1
또한 프로세스 대체 구문 <(command)은 Bash 및 기타에서 작동하지만 POSIX 셸로 이식 할 수 없습니다.
tripleee

8
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

이은보다 더 노력하고 comm그것의 각 라인 검색으로 명령 file1file2어디에 comm줄 경우에만 비교합니다 n에서 file1IS 라인에 동일 n의를 file2.
teriiehina

1
@teriiehina : 아니오; comm단순히 file1의 N 행을 file2의 N 행과 비교하지는 않습니다. 파일에 삽입 된 일련의 라인을 완벽하게 관리 할 수 ​​있습니다 (물론 다른 파일에서 일련의 라인을 삭제하는 것과 같습니다). 입력이 정렬 순서대로되어 있으면됩니다.
Jonathan Leffler

comm주문을 유지하려면 답변 보다 낫습니다 . awk중복을 원하지 않으면 대답 보다 낫습니다 .
tuxayo

설명이 여기에 있습니다 : stackoverflow.com/questions/17552789/...
크리스 Koknat


3

Linux의 제한된 버전 (QNAP (nas)와 같은)은 다음과 같습니다.

  • 통신은 존재하지 않았다
  • grep -f file1 file2@ChristopherSchultz가 말한 것처럼 일부 문제가 발생할 수 있으며 사용 grep -F -f file1 file2이 실제로 느려졌습니다 (5 분 이상-완료되지 않음-아래 방법으로 20MB가 넘는 파일에서 2-3 초 이상)

그래서 여기 내가 한 일이 있습니다.

sort file1 > file1.sorted
sort file2 > file2.sorted

diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted

files.same.sorted원래의 순서와 동일한 순서로되어 있다면 file1과 동일한 순서로이 행을 추가하십시오.

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same

또는 file2와 동일한 순서로 :

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same

2

누군가가 여러 파일에 대해이 작업을 수행하는 방법을 계속 찾고 있다면 많은 파일에서 일치하는 줄 찾기에 대한 링크 된 답변을 참조하십시오 .


이 두 가지 답변 ( ans1ans2 )을 결합 하면 파일을 정렬하지 않고도 필요한 결과를 얻을 수 있다고 생각합니다.

#!/bin/bash
ans="matching_lines"

for file1 in *
do 
    for file2 in *
        do 
            if  [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
                echo "Comparing: $file1 $file2 ..." >> $ans
                perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
            fi
         done 
done

간단히 저장하고 실행 권한을 부여하고 ( chmod +x compareFiles.sh) 실행하십시오. 현재 작업 디렉토리에있는 모든 파일을 가져 와서 "matching_lines"파일에 결과를 남겨두고 모든 비교를 수행합니다.

개선 사항 :

  • 디렉토리 건너 뛰기
  • 모든 파일을 두 번 비교하지 마십시오 (file1과 file2 및 file2와 file1).
  • 일치하는 문자열 옆에 줄 번호를 추가하십시오.

-2
rm file3.txt

cat file1.out | while read line1
do
        cat file2.out | while read line2
        do
                if [[ $line1 == $line2 ]]; then
                        echo $line1 >>file3.out
                fi
        done
done

그렇게해야합니다.


1
rm -f file3.txt파일을 삭제하려는 경우 사용해야 합니다. 파일이 존재하지 않으면 오류가보고되지 않습니다. OTOH, 스크립트가 단순히 표준 출력으로 에코되어 스크립트 사용자가 출력 위치를 선택할 수 있도록하는 경우 필요하지 않습니다. 궁극적 으로 고정 파일 이름 ( 및 ) 대신 $1$2(명령 줄 인수) 를 사용하려고합니다 . 그것은 알고리즘을 떠난다 : 그것은 느려질 것이다. 의 각 줄에 대해 한 번씩 읽 습니다 . 파일이 큰 경우 (예 : 여러 킬로바이트) 속도가 느려집니다. file1.outfile2.outfile2.outfile1.out
Jonathan Leffler

쉘 메타 문자를 포함하지 않는 입력이 있으면 명목상으로 작동 할 수 있지만 (힌트 : shellcheck.net 에서 어떤 경고가 표시되는지 확인하십시오 )이 순진한 접근 방식은 대단히 비효율적입니다. grep -F하나의 파일을 메모리로 읽은 다음 다른 파일을 한 번만 통과 하는 도구는 두 입력 파일 모두에서 반복적으로 반복되는 것을 피합니다.
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.