파일이 있습니다 f1
.
line1
line2
line3
line4
..
..
다른 파일에있는 모든 줄을 삭제하고 싶습니다 f2
.
line2
line8
..
..
내가 의도 한 것과는 거리가 먼 cat
및으로 무언가를 시도했습니다 sed
. 어떻게 할 수 있습니까?
파일이 있습니다 f1
.
line1
line2
line3
line4
..
..
다른 파일에있는 모든 줄을 삭제하고 싶습니다 f2
.
line2
line8
..
..
내가 의도 한 것과는 거리가 먼 cat
및으로 무언가를 시도했습니다 sed
. 어떻게 할 수 있습니까?
답변:
grep -v -x -f f2 f1
트릭을해야합니다.
설명:
-v
일치하지 않는 라인을 선택하려면-x
전체 라인 만 일치-f f2
패턴을 얻기 위해 f2
대신 패턴 대신에 고정 된 문자열 을 사용 grep -F
하거나 fgrep
일치 시킬 수 있습니다 (줄 을 정규식 패턴으로 처리하는 대신 "당신이 얻는 것"방식으로 줄을 제거하려는 경우 ).f2
f2
grep
. f2
검색을 시작하기 전에 제대로 전처리 하면 검색에 O (n) 시간 만 걸립니다.
대신 comm을 시도하십시오 (f1 및 f2가 "이미 정렬"되었다고 가정).
comm -2 -3 f1 f2
comm
솔루션이 질문 나타냅니다 않습니다이 점에서 선 f1
사용의 전제 조건 인 분류되어 있습니다comm
comm -2 -3 <(sort f1) <(sort f2)
너무 크지 않은 제외 파일의 경우 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 크기)입니다.
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
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 솔루션은 정렬보다 훨씬 빠르며 원래 순서를 유지하는 것을 선호했습니다.
f
보다 명확하다는 NR == FNR
것을 알지만 그것은 취향의 문제입니다. 해시에 대한 할당은 너무 빨라야 두 버전간에 측정 가능한 속도 차이가 없습니다. 나는 복잡성에 대해 틀렸다고 생각합니다. 조회가 일정하다면 업데이트도 일정해야합니다 (평균적으로). 업데이트가 대수라고 생각한 이유를 모르겠습니다. 내 대답을 편집하겠습니다.
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
솔루션은 빈 제외 파일로 실패하고 하나만 사용할 수 있습니다.
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
생성 된 두 파일간에 차이가 없음을 보여주기 위해 사용되었습니다.
다양한 다른 답변 간의 타이밍 비교 :
$ 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
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
'프로그래밍'답변은 아니지만 여기에 빠르고 더러운 해결책이 있습니다. http://www.listdiff.com/compare-2-lists-difference-tool 로 이동 하십시오 .
분명히 대용량 파일에서는 작동하지 않지만 나를 위해 트릭을 수행했습니다. 몇 가지 참고 :