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


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.