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

파일이 있습니다 f1.

line1
line2
line3
line4
..
..

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

line2
line8
..
..

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



답변

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

설명:

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

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


답변

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

comm -2 -3 f1 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 크기)입니다.


답변

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 솔루션은 정렬보다 훨씬 빠르며 원래 순서를 유지하는 것을 선호했습니다.


답변

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