두 개의 큰 파일 (파일 이름 집합)이 있습니다. 각 파일에 약 30.000 줄이 있습니다. file2에없는 file1에서 줄을 찾는 빠른 방법을 찾으려고합니다.
예를 들어, 이것이 file1 인 경우 :
line1
line2
line3
그리고 이것은 file2입니다.
line1
line4
line5
그런 다음 내 결과 / 출력은 다음과 같아야합니다.
line2
line3
이것은 작동합니다 :
grep -v -f file2 file1
그러나 큰 파일에 사용하면 매우 느립니다.
나는이 사용 DIFF ()를 할 수있는 좋은 방법이 의심되지만 출력은 없어야 단지 다른 라인, 아무것도, 나는 그것을 위해 스위치를 찾을 수 없습니다.
bash 및 기본 Linux 바이너리를 사용 하여이 작업을 수행하는 빠른 방법을 찾도록 도와 줄 수 있습니까?
편집 : 내 자신의 질문에 후속 조치로, diff ()를 사용하여 지금까지 찾은 가장 좋은 방법입니다.
diff file2 file1 | grep '^>' | sed 's/^>\ //'
더 좋은 방법이 있어야합니까?
답변
GNU diff
출력 에서 이전 / 새 / 변경되지 않은 행의 형식을 제어하여이를 달성 할 수 있습니다 .
diff --new-line-format="" --unchanged-line-format="" file1 file2
이 작업을 수행하려면 입력 파일 을 정렬해야합니다 . bash
(및 zsh
)을 사용하면 프로세스 대체를 사용하여 적절하게 정렬 할 수 있습니다 <( )
.
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
위의 새 줄 과 변경되지 않은 줄은 표시 되지 않으므로 변경된 줄만 출력됩니다 (예 : 제거 된 줄). 당신은 또한 몇 가지 사용할 수 있습니다 diff
같은 다른 솔루션이 제공하지 않는 옵션 -i
의 경우, 또는 다양한 공백 옵션 (무시 -E
, -b
, -v
덜 엄격한 매칭 등).
설명
옵션은 --new-line-format
, --old-line-format
그리고 --unchanged-line-format
당신이 방법은 제어 할 수 diff
비슷한 차이, 포맷 printf
형식 지정자를. 이 옵션은 각각 새 (추가), 이전 (제거) 및 변경되지 않은 행 을 형식화합니다 . 1을 비워 “”로 설정하면 해당 종류의 행이 출력되지 않습니다.
통합 diff 형식에 익숙한 경우 다음을 사용하여 부분적으로 다시 만들 수 있습니다.
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
%L
지정 문제의 선이며, 우리는 “+” “각 접두사 -처럼,”또는 “” diff -u
(그것은 단지 출력 차이, 그것이 부족합니다 ---
+++
및 @@
각 그룹화 변화의 상단 선). 당신은 또한 같은 다른 유용한 일을하려면이 옵션을 사용할 수있는 번호를 각 라인 에 %dn
.
이 diff
방법은 (다른 제안 comm
과 함께 및 join
) 정렬 된 입력 으로 예상 출력 만 생성 하지만 <(sort ...)
정렬 하는 데 사용할 수 있습니다 . 여기에 간단 awk
(nawk) 스크립트 (스크립트에 의해 영감은 연결된에 Konsolebox의 대답) 임의의 입력 파일을 정렬 받아들이, 그리고 그들이 FILE1에서 발생하는 순서에 누락 된 라인을 출력합니다.
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
이것은 file1의 전체 내용을 한 줄씩 색인 배열 ll1[]
로 저장하고 file2의 전체 내용을 한 줄씩 색인 연관 배열로 저장 ss2[]
합니다. 두 파일을 모두 읽은 후 반복 ll1
하여 in
연산자를 사용하여 file1의 행이 file2에 있는지 판별하십시오. ( diff
중복이 있으면 메소드 와 다른 출력을 갖습니다 .)
파일이 충분히 커서 파일을 모두 저장하는 데 메모리 문제가 발생하는 경우 file1 만 저장하고 file2를 읽는 동안 일치 항목을 삭제하여 CPU를 메모리로 교환 할 수 있습니다.
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
두 어레이의 상기 저장 FILE1의 전체 내용을, 행 번호에 의해 인덱싱 하나 ll1[]
, 광고 내용에 의해 인덱싱 하나 ss1[]
. 그런 다음 file2를 읽으면 일치하는 각 줄이 ll1[]
및 에서 삭제됩니다 ss1[]
. 마지막에는 원래 순서를 유지하면서 file1의 나머지 행이 출력됩니다.
이 경우 언급 한 문제로 GNU를 사용하여 나누고 정복 할 수 있습니다 split
(필터링은 GNU 확장입니다).
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
의 사용 및 배치 참고 -
의미 stdin
상의 gawk
명령 줄을. 이것은 split
호출 당 20000 라인 청크 단위로 file1에서 제공 됩니다.
비 GNU 시스템에 대한 사용자의 경우, 거의 확실하게 존재는 GNU로 coreutils이의 일환으로 OSX에 포함, 당신이 얻을 수있는 패키지 애플의 Xcode GNU를 제공하는 도구 diff
, awk
하지만 단지 POSIX / BSD split
가 아닌 GNU 버전.
답변
통신의 (짧은 “일반”에 대한) 명령을 유용 할 수 있습니다comm - compare two sorted files line by line
#find lines only in file1
comm -23 file1 file2
#find lines only in file2
comm -13 file1 file2
#find lines common to both files
comm -12 file1 file2
man
파일은 실제로 이것에 대한 아주 읽을 수 있습니다.
답변
Konsolebox가 제안한 것처럼, 포스터 grep 솔루션
grep -v -f file2 file1
단순히 -F
옵션을 추가 하면 패턴을 정규 표현식 대신 고정 문자열로 처리하기 위해 실제로 훌륭하게 작동 합니다. 나는 ~ 1000 줄 파일 목록에서 이것을 비교해야한다고 검증했다. 으로 -F
는 (실제) 2.278의했다없이하면서 (실제), 0.031의했다,에 그렙 출력을 리디렉션 할 때 wc -l
.
이러한 테스트 -x
에는 file2가 file1의 하나 이상의 행 중 일부만 일치하는 행을 포함하는 경우 완전히 정확도를 보장하기 위해 솔루션의 일부인 스위치 도 포함 되었습니다.
따라서 입력을 정렬 할 필요가없는 빠르고 유연한 솔루션 (대소 문자 구분 등)은 다음과 같습니다.
grep -F -x -v -f file2 file1
모든 버전의 grep에서 작동하지는 않습니다. 예를 들어 macOS에서는 실패합니다. 파일 1의 행이 파일의 하위 문자열 인 다른 행과 일치하더라도 파일 2에 존재하지 않는 것으로 표시됩니다. . 또는 이 솔루션을 사용하기 위해 macOS 에 GNU grep을 설치할 수 있습니다 .
답변
정렬 및 diff의 속도는 얼마입니까?
sort file1 -u > file1.sorted
sort file2 -u > file2.sorted
diff file1.sorted file2.sorted
답변
당신이 최소한의 리눅스 배포판의 예 : “멋진 도구”짧은 경우,이 단지와 솔루션입니다 cat
, sort
및 uniq
:
cat includes.txt excludes.txt excludes.txt | sort | uniq --unique
테스트:
seq 1 1 7 | sort --random-sort > includes.txt
seq 3 1 9 | sort --random-sort > excludes.txt
cat includes.txt excludes.txt excludes.txt | sort | uniq --unique
# Output:
1
2
이것 에 비해 비교적 빠릅니다 grep
.
답변
$ join -v 1 -t '' file1 file2
line2
line3
는 -t
당신이 라인의 일부에 공백이 있다면 그것은 전체 라인을 비교 있는지 확인합니다.
답변
파이썬을 사용할 수 있습니다 :
python -c '
lines_to_remove = set()
with open("file2", "r") as f:
for line in f.readlines():
lines_to_remove.add(line.strip())
with open("f1", "r") as f:
for line in f.readlines():
if line.strip() not in lines_to_remove:
print(line.strip())
'
