다른 파일 A에서 파일 B에 나타나는 줄을 제거하는 방법?


160

메일마다 한 줄씩 큰 파일 A (이메일로 구성)가 있습니다. 또한 다른 메일 세트를 포함하는 다른 파일 B 가 있습니다.

파일 A에서 파일 B에 나타나는 모든 주소를 제거하기 위해 어떤 명령을 사용합니까?

따라서 파일 A에 포함 된 경우 :

A
B
C

파일 B에는 다음이 포함됩니다.

B    
D
E

그런 다음 파일 A는 다음과 같이 남겨 두어야합니다.

A
C

이제 이것이 더 자주 묻는 질문이라는 것을 알고 있지만 온라인 에서 하나의 명령 만 발견 하여 잘못된 구분 기호가있는 오류를 발견했습니다.

어떤 도움이라도 대단히 감사하겠습니다! 누군가는 분명히 영리한 원 라이너를 내놓을 것이지만 저는 쉘 전문가가 아닙니다.



1
여기에 대한 답변이 정렬 된 파일에 대한 것이고 가장 명백한 것이 누락 된 경우, 물론 당신의 잘못은 아니지만 다른 것이 더 일반적으로 유용합니다.
tripleee

답변:


204

파일이 정렬 된 경우 (예 : 파일에 있음) :

comm -23 file1 file2

-23두 파일 모두 또는 파일 2에만있는 행을 표시하지 않습니다. 파일이 정렬되지 않은 경우 먼저 파일을 통해 파이프하십시오 sort.

참고 항목 여기 사람이 페이지를


8
comm -23 file1 file2 > file3file2가 아닌 file1의 내용을 file3으로 출력합니다. 그런 다음 mv file3 file1file1에서 중복 내용을 지 웁니다.
Spectral

2
또는을 사용하십시오 comm -23 file1 file2 | sponge file1. 정리할 필요가 없습니다.
Socowi

매뉴얼 페이지 링크가 저를 위해로드되지 않습니다 – 대안 : linux.die.net/man/1/comm
Felix Rabe

@Socowi 스폰지 란? 내 시스템에는 없습니다. (macos 10.13)
Felix Rabe

@FelixRabe, 그거 참 귀찮습니다. 귀하의 링크로 교체되었습니다. 감사합니다
Archetypal Paul

85

grep -Fvxf <lines-to-remove> <all-lines>

  • 분류되지 않은 파일에서 작동
  • 순서를 유지
  • POSIX입니다

예:

cat <<EOF > A
b
1
a
0
01
b
1
EOF

cat <<EOF > B
0
1
EOF

grep -Fvxf B A

산출:

b
a
01
b

설명:

  • -F: 기본 BRE 대신 리터럴 문자열을 사용하십시오.
  • -x: 전체 줄과 일치하는 항목 만 고려
  • -v: 일치하지 않는 인쇄
  • -f file: 주어진 파일에서 패턴을 가져옵니다

이 방법은 다른 방법보다 미리 정렬 된 파일에서 속도가 느립니다. 더 일반적이기 때문입니다. 속도도 중요하다면, 한 파일에서 다른 파일에없는 행을 찾는 빠른 방법을 참조하십시오.

인라인 작업을위한 빠른 bash 자동화는 다음과 같습니다.

remove-lines() (
  remove_lines="$1"
  all_lines="$2"
  tmp_file="$(mktemp)"
  grep -Fvxf "$remove_lines" "$all_lines" > "$tmp_file"
  mv "$tmp_file" "$all_lines"
)

GitHub의 상류 .

용법:

remove-lines lines-to-remove remove-from-this-file

참조 : /unix/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in-another


55

구조에 awk!

이 솔루션에는 정렬 된 입력이 필요하지 않습니다. 먼저 fileB를 제공해야합니다.

awk 'NR==FNR{a[$0];next} !($0 in a)' fileB fileA

보고

A
C

어떻게 작동합니까?

NR==FNR{a[$0];next} 관용구는 첫 번째 파일을 나중에 "포함하는"테스트를 위해 키로 연관 배열에 저장하기위한 것입니다.

NR==FNR 전역 라인 카운터 (NR)가 현재 파일 라인 카운터 (FNR)와 동일한 첫 번째 파일을 스캔하고 있는지 확인합니다.

a[$0] 현재 행을 연관 배열에 키로 추가합니다. 이것은 중복 값 (키)이없는 집합처럼 동작합니다.

!($0 in a)우리는 이제 다음 파일에 in있으며, 포함 테스트입니다. 여기서 현재 줄이 첫 번째 파일의 첫 번째 단계에서 채워진 세트에 있는지 확인 !하고 조건을 무시합니다. 여기서 누락 된 작업은 기본적 {print}으로 명시 적으로 작성되지 않은 작업입니다.

블랙리스트에 포함 된 단어를 제거하는 데 사용할 수 있습니다.

$ awk '...' badwords allwords > goodwords

약간의 변경으로 여러 목록을 정리하고 정리 된 버전을 만들 수 있습니다.

$ awk 'NR==FNR{a[$0];next} !($0 in a){print > FILENAME".clean"}' bad file1 file2 file3 ...

이것에 대한 전체 표시. Windows에서 GnuWin32의 명령 행에서 이것을 사용하려면 작은 니블을 큰 따옴표로 바꾸십시오. 치료를합니다. 많은 감사합니다.
twobob

이것은 작동하지만 어떻게 A (새 줄로) B의 형태로 출력을 fileA로 리디렉션 할 수 있습니까? B
Anand Builders

내 생각 엔 A\nC, 임시 파일에 먼저 쓰고 원본 파일을 덮어 씁니다... > tmp && mv tmp fileA
karakfa

나도 이것에 대한 전체 표시. 이 awk는 104,000 개의 항목이있는 파일을 처리하는 데 1 초가 걸립니다. +1 :
MitchellK

스크립트에서 이것을 사용할 때 fileB비어 있지 않은 (0 바이트 길이) 인지 먼저 확인하십시오. 만약 그렇지 않으면의 예상 내용 대신 빈 결과가 나오기 때문입니다 fileA. (원인 : 그때에 FNR==NR적용됩니다 fileA.)
Peter Nowee


7

파일이 정렬되어 있지 않으면이 작업을 수행 할 수 있습니다

diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a

--new-line-format파일 b에 있지만 a --old-..가 아닌 행은 파일 a에 있지만 b --unchanged-..는 아닌 행은 둘 다에있는 행입니다. %L선이 정확하게 인쇄되도록합니다.

man diff

상세 사항은


1
파일을 정렬하지 않으면 이것이 작동한다고 말합니다. 정렬하면 어떤 문제가 발생합니까? 부분적으로 정렬하면 어떻게 되나요?
Carlos Macasaet

1
그것은 comm명령 사용법을 제안한 위의 솔루션에 대한 응답이었습니다 . comm파일을 정렬해야하므로 정렬 된 경우 해당 솔루션을 사용할 수도 있습니다. 파일의 정렬 여부에 관계없이이 솔루션을 사용할 수 있습니다.
aec

7

@karakfa의 멋진 대답의 미세 조정은 매우 큰 파일의 경우 훨씬 빠릅니다. 이 답변과 마찬가지로 파일을 정렬 할 필요는 없지만 awk의 연관 배열 덕분에 속도가 보장됩니다. 조회 파일 만 메모리에 보관됩니다.

이 공식은 또한 입력 파일에서 하나의 특정 필드 ($ N) 만 비교에 사용될 가능성을 허용합니다.

# Print lines in the input unless the value in column $N
# appears in a lookup file, $LOOKUP;
# if $N is 0, then the entire line is used for comparison.

awk -v N=$N -v lookup="$LOOKUP" '
  BEGIN { while ( getline < lookup ) { dictionary[$0]=$0 } }
  !($N in dictionary) {print}'

(이 방법의 또 다른 장점은 선행 및 후행 공백을 트리밍하는 등 비교 기준을 쉽게 수정할 수 있다는 것입니다.)


이것은 다른 하나의 라이너보다 코너 케이스 크로스 플랫폼 시나리오에서 사용하기가 더 어렵습니다. 그러나 성능 노력을
싫어합니다

2

파이썬을 사용할 수 있습니다 :

python -c '
lines_to_remove = set()
with open("file B", "r") as f:
    for line in f.readlines():
        lines_to_remove.add(line.strip())

with open("file A", "r") as f:
    for line in [line.strip() for line in f.readlines()]:
        if line not in lines_to_remove:
            print(line)
'

2

당신이 사용할 수있는 - diff fileA fileB | grep "^>" | cut -c3- > fileA

이것은 정렬되지 않은 파일에도 적용됩니다.


-1

두 파일 사이에서 공통 행을 제거하려면 grep, comm 또는 join 명령을 사용할 수 있습니다.

grep은 작은 파일에만 작동합니다. -v와 -f를 함께 사용하십시오.

grep -vf file2 file1 

file2의 행과 일치하지 않는 file1의 행을 표시합니다.

comm은 어휘 정렬 된 파일에서 작동하는 유틸리티 명령입니다. 두 개의 파일을 입력으로 사용하고 세 개의 텍스트 열을 출력으로 생성합니다. 첫 번째 파일에만 줄; 두 번째 파일에서만 줄; 및 두 파일의 줄. 그에 따라 -1, -2 또는 -3 옵션을 사용하여 열의 인쇄를 억제 할 수 있습니다.

comm -1 -3 file2 file1

file2의 행과 일치하지 않는 file1의 행을 표시합니다.

마지막으로 지정된 파일에 대해 동등 결합을 수행하는 유틸리티 명령 인 join이 있습니다. -v 옵션을 사용하면 두 파일 사이의 공통 줄을 제거 할 수도 있습니다.

join -v1 -v2 file1 file2

이 모든 것은 이미 다른 답변으로 주어졌습니다. 귀하의 GREP 하나는 -F를 필요로하거나 라인이 regexps '에 같이 할 때 이상한 결과를 얻을 수 있습니다
전형적인 폴
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.