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

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

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

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

A
B
C

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

B    
D
E

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

A
C

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

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



답변

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

comm -23 file1 file2

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

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


답변

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


답변

구조에 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 ...


답변

동일한 작업을 수행하는 다른 방법 (정렬 된 입력도 필요함) :

join -v 1 fileA fileB

Bash에서 파일이 미리 정렬되지 않은 경우 :

join -v 1 <(sort fileA) <(sort fileB)


답변

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

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

상세 사항은


답변

@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}'

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


답변

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

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)
'