다른 파일에있는 한 파일에서 줄 삭제


126

파일이 있습니다 f1.

line1
line2
line3
line4
..
..

다른 파일에있는 모든 줄을 삭제하고 싶습니다 f2.

line2
line8
..
..

내가 의도 한 것과는 거리가 먼 cat및으로 무언가를 시도했습니다 sed. 어떻게 할 수 있습니까?



당신은 (예를 부분적으로 일치하는 경우) 다른 파일에서 문자열을 "도 포함"하는 파일에서 줄을 제거보기 위해 찾고 있다면 unix.stackexchange.com/questions/145079/...
— rogerdpack

답변:


154

grep -v -x -f f2 f1 트릭을해야합니다.

설명:

  • -v 일치하지 않는 라인을 선택하려면
  • -x 전체 라인 만 일치
  • -f f2 패턴을 얻기 위해 f2

대신 패턴 대신에 고정 된 문자열 을 사용 grep -F하거나 fgrep일치 시킬 수 있습니다 (줄 을 정규식 패턴으로 처리하는 대신 "당신이 얻는 것"방식으로 줄을 제거하려는 경우 ).f2f2


22
이것은 O (n²) 복잡성을 가지고 있으며 파일에 K 줄이 몇 개 이상 포함되면 완료하는 데 몇 시간이 걸리기 시작합니다.
— Arnaud Le Blanc

11
어떤 SO 제안 알고리즘이 O (n ^ 2) 복잡도를 갖는지 알아내는 것은 O (n) 복잡도 만 가지고 있지만 경쟁하는 데 몇 시간이 걸릴 수 있습니다.
— HDave

2
나는 각각 ~ 2k 줄의 2 개의 파일에서 이것을 시도했고 OS에 의해 죽었습니다 (허용, 이것은 그렇게 강력하지 않지만 여전히).
— Trebor Rude 2014

1
나는 이것의 우아함을 좋아한다. 나는 Jona Christopher Sahnwal의 대답 속도를 선호합니다.
— Alex Hall

1
@ arnaud576875 : 확실합니까? 의 구현에 따라 다릅니다 grep. f2검색을 시작하기 전에 제대로 전처리 하면 검색에 O (n) 시간 만 걸립니다.
— HelloGoodbye

57

대신 comm을 시도하십시오 (f1 및 f2가 "이미 정렬"되었다고 가정).

comm -2 -3 f1 f2

5
나는 확실하지 않다 comm솔루션이 질문 나타냅니다 않습니다이 점에서 선 f1사용의 전제 조건 인 분류되어 있습니다comm
— gabuzo

1
내 파일이 정렬되고 그중 하나에는 250,000 개 이상의 줄이 있고 다른 하나에는 28,000 개만 포함되어 있었기 때문에 이것은 저에게 효과적이었습니다. 감사!
— Winter

1
이것이 작동하면 (입력 파일이 정렬 됨) 매우 빠릅니다!
— Mike Jarvis 2015 년

arnaud576875의 솔루션에서와 같이 cygwin을 사용하는 경우 두 번째 파일에서 보관할 수있는 중복 줄이 제거되었습니다.
— Alex Hall

9
: 당신은 물론, 먼저 파일을 정렬하는 과정 대체를 사용할 수 있습니다comm -2 -3 <(sort f1) <(sort f2)
— davemyron

14

너무 크지 않은 제외 파일의 경우 AWK의 연관 배열을 사용할 수 있습니다.

awk 'NR == FNR { list[tolower($0)]=1; next } { if (! list[tolower($0)]) print }' exclude-these.txt from-this.txt 

출력은 "from-this.txt"파일과 동일한 순서입니다. 이 tolower()함수는 필요한 경우 대소 문자를 구분하지 않습니다.

알고리즘 복잡성은 아마도 O (n) (exclude-these.txt 크기) + O (n) (from-this.txt 크기)입니다.


너무 크지 않은 파일을 말하는 이유는 무엇입니까? 여기서 두려움은 해시를 생성하기 위해 시스템 메모리에서 시스템을 실행하는 awk입니까, 아니면 다른 제한이 있습니까?
— rogerdpack

추종자에게는 라인을 "삭제"하는 훨씬 더 공격적인 옵션이 있습니다 (비교 배열을 사용하려면 비교가 정확해야하므로), ex unix.stackexchange.com/a/145132/8337
— rogerdpack

@rogerdpack : 큰 제외 파일에는 큰 해시 배열 (및 긴 처리 시간)이 필요합니다. 큰 "from-this.txt"는 긴 처리 시간 만 필요합니다.
— 추후 공지가있을 때까지 일시 중지되었습니다.

1
exclude-these.txt비어 있으면 실패합니다 (즉, 출력을 생성하지 않음) . 이 경우 @ jona-christopher-sahnwaldt의 답변이 작동합니다. 또한 여러 파일 등을 지정할 수 있습니다awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 done.out failed.out f=2 all-files.out
— 그레이엄 러셀

11

Dennis Williamson의 대답과 유사합니다 (대부분 구문 변경, 예를 들어 NR == FNR트릭 대신 명시 적으로 파일 번호 설정 ).

awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 exclude-these.txt f=2 from-this.txt

액세스 r[$0]하면 해당 행에 대한 항목이 생성되며 값을 설정할 필요가 없습니다.

awk가 일정한 조회와 (평균적으로) 일정한 업데이트 시간이있는 해시 테이블을 사용한다고 가정하면이 시간 복잡도는 O (n + m)가 될 것입니다. 여기서 n과 m은 파일의 길이입니다. 제 경우에는 n은 ~ 25 백만이고 m은 ~ 14000입니다. awk 솔루션은 정렬보다 훨씬 빠르며 원래 순서를 유지하는 것을 선호했습니다.


이것이 Dennis Williamson의 답변과 어떻게 다른가요? 해시에 할당하지 않는다는 유일한 차이점은 이것보다 약간 빠릅니까? 알고리즘 복잡성은 그의 것과 동일합니까?
— rogerdpack

차이점은 대부분 구문입니다. 나는 변수가 f보다 명확하다는 NR == FNR것을 알지만 그것은 취향의 문제입니다. 해시에 대한 할당은 너무 빨라야 두 버전간에 측정 가능한 속도 차이가 없습니다. 나는 복잡성에 대해 틀렸다고 생각합니다. 조회가 일정하다면 업데이트도 일정해야합니다 (평균적으로). 업데이트가 대수라고 생각한 이유를 모르겠습니다. 내 대답을 편집하겠습니다.
— jcsahnwaldt Monica 복원

나는 이러한 답변을 많이 시도했고 이것은 AMAZEBALLS 빠르다. 수십만 줄의 파일이 있습니다. 매력처럼 일했다!
— 미스터 T

1
이것이 제가 선호하는 솔루션입니다. 여러 파일과 함께 작동하며 예를 들어 awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 empty.file done.out failed.out f=2 all-files.out. 다른 awk솔루션은 빈 제외 파일로 실패하고 하나만 사용할 수 있습니다.
— Graham Russell

5

Ruby (1.9 이상)가있는 경우

#!/usr/bin/env ruby 
b=File.read("file2").split
open("file1").each do |x|
  x.chomp!
  puts x if !b.include?(x)
end

O (N ^ 2) 복잡성이 있습니다. 성능에 관심이 있다면 여기에 다른 버전이 있습니다.

b=File.read("file2").split
a=File.read("file1").split
(a-b).each {|x| puts x}

뺄셈에 영향을주기 위해 해시를 사용하므로 복잡성 O (n) (a의 크기) + O (n) (b의 크기)

여기 user576875가 제공 한 약간의 벤치 마크가 있지만 위의 10 만 줄이 있습니다.

$ for i in $(seq 1 100000); do echo "$i"; done|sort --random-sort > file1
$ for i in $(seq 1 2 100000); do echo "$i"; done|sort --random-sort > file2
$ time ruby test.rb > ruby.test

real    0m0.639s
user    0m0.554s
sys     0m0.021s

$time sort file1 file2|uniq -u  > sort.test

real    0m2.311s
user    0m1.959s
sys     0m0.040s

$ diff <(sort -n ruby.test) <(sort -n sort.test)
$

diff 생성 된 두 파일간에 차이가 없음을 보여주기 위해 사용되었습니다.


1
이것은 O (n²) 복잡성을 가지고 있으며 파일에 K 줄이 몇 개 이상 포함되면 완료하는 데 몇 시간이 걸리기 시작합니다.
— Arnaud Le Blanc

나는 큰 파일을 언급하지 않았기 때문에이 시점에서별로 신경 쓰지 않는다.
— kurumi 2011 년

3
그렇게 방어적일 필요는 없습니다. @ user576875가 귀하의 답변이나 다른 것에 대해 반대표를 던진 것처럼 보이지 않습니다. :-)
— John Parker

아주 좋은 두 번째 버전, 루비가 이겼습니다 :)
— Arnaud Le Blanc

4

다양한 다른 답변 간의 타이밍 비교 :

$ for n in {1..10000}; do echo $RANDOM; done > f1
$ for n in {1..10000}; do echo $RANDOM; done > f2
$ time comm -23 <(sort f1) <(sort f2) > /dev/null

real    0m0.019s
user    0m0.023s
sys     0m0.012s
$ time ruby -e 'puts File.readlines("f1") - File.readlines("f2")' > /dev/null

real    0m0.026s
user    0m0.018s
sys     0m0.007s
$ time grep -xvf f2 f1 > /dev/null

real    0m43.197s
user    0m43.155s
sys     0m0.040s

sort f1 f2 | uniq -u 두 파일에서 여러 번 나타나는 줄을 제거하기 때문에 대칭적인 차이도 아닙니다.

comm은 stdin 및 여기 문자열과 함께 사용할 수도 있습니다.

echo $'a\nb' | comm -23 <(sort) <(sort <<< $'c\nb') # a

2

SQLite 셸에 적합한 작업 인 것 같습니다.

create table file1(line text);
create index if1 on file1(line ASC);
create table file2(line text);
create index if2 on file2(line ASC);
-- comment: if you have | in your files then specify “ .separator ××any_improbable_string×× ”
.import 'file1.txt' file1
.import 'file2.txt' file2
.output result.txt
select * from file2 where line not in (select line from file1);
.q

1

당신은 시도해 봤어 이 나오지도 함께?

sed 's#^#sed -i '"'"'s%#g' f2 > f2.sh

sed -i 's#$#%%g'"'"' f1#g' f2.sh

sed -i '1i#!/bin/bash' f2.sh

sh f2.sh

0

'프로그래밍'답변은 아니지만 여기에 빠르고 더러운 해결책이 있습니다. http://www.listdiff.com/compare-2-lists-difference-tool 로 이동 하십시오 .

분명히 대용량 파일에서는 작동하지 않지만 나를 위해 트릭을 수행했습니다. 몇 가지 참고 :

  • 나는 어떤 식 으로든 웹 사이트와 관련이 없습니다 (아직도 나를 믿지 않는다면 온라인에서 다른 도구를 검색 할 수 있습니다. "온라인 차이 목록 설정"이라는 검색어를 사용했습니다)
  • 링크 된 웹 사이트는 모든 목록 비교에서 네트워크 호출을하는 것처럼 보이므로 민감한 데이터를 제공하지 마십시오.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.