İki dosyada ortak olan satırları bulmak için Unix komutu


179

Eminim bir keresinde iki veya daha fazla dosyadan ortak satırları yazdırabilecek bir unix komutu buldum, kimse adını biliyor mu? Bundan çok daha basitti diff.


5
commSıralı girdi dosyaları gerektirdiğinden , bu sorunun yanıtları herkesin istediği şey olmayabilir . Sadece satır satır ortak olmasını istiyorsanız, bu harika. Ama "anti-diff" dediğim şeyi istiyorsanız, commbu işi yapmaz.
Robert P. Goldman

@ RobertP.Goldman, dosya1 gibi kısmi desen pr-123-xy-45içerdiğinde ve dosya2 içerdiğinde iki dosya arasında ortak olmanın bir yolu vardır ec11_orop_pr-123-xy-45.gz. Ben file3 içeren gerekirec11_orop_pr-123-xy-45.gz
Chandan Choudhury

Metin dosyalarını satır satır sıralamak için buna bakın
y2k-shubham

Yanıtlar:


216

Aradığın komut comm. Örneğin:-

comm -12 1.sorted.txt 2.sorted.txt

Buraya:

-1 : sütun 1'i bastırma (1.sorted.txt'ye özgü satırlar)

-2 : sütun 2'yi bastırma (2.sorted.txt'ye özgü satırlar)


27
Tipik kullanım: comm -12 1.sorted.txt 2.sorted.txt
Fedir RYKHTIK

45
Comm'un sıralanmış dosyalara ihtiyacı olsa da, her iki dosyanın ortak satırlarını almak için grep -f file1 file2 dosyasını alabilirsiniz.
ferdy

2
@ferdy (Cevabınızdaki yorumumu tekrarlamak, sizinki aslında bir yorum olarak gönderilen tekrarlanan bir cevaptır) grepbeklemeyebileceğiniz bazı garip şeyler yapar. Özellikle, her şey 1.txtdüz bir dize olarak değil, normal bir ifade olarak yorumlanacaktır. Ayrıca, herhangi bir boş satır içindeki 1.txttüm satırlarla eşleşir 2.txt. Bu yüzden grepsadece çok özel durumlarda çalışacaktır. En azından kullanmak isteyeceksiniz fgrep(veya grep -f) ama boş satır muhtemelen bu süreçte tahribat yaratacak.
Christopher Schultz

11
Bkz ferdy 'ın cevabı aşağıda ve Christopher Schultz ' Üzerinde ler ve açıklamalarım. TL; DR - kullanımı grep -F -x -f file1 file2.
Jonathan Leffler

1
@bapors: Komuttan çıktıyı comm3 ayrı dosyaya nasıl alabilirim ? Cevap buraya rahatça sığamayacak kadar büyüktü.
Jonathan Leffler

62

Comm komutunu sıralanmamış dosyalara kolayca uygulamak için Bash'in işlem değiştirmesini kullanın :

$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321

Yani abc ve def dosyalarının ortak bir satırı, "132" olanı vardır. Sıralanmamış dosyalarda comm özelliğini kullanma :

$ comm abc def
123
    132
567
132
    777
    321
$ comm -12 abc def # No output! The common line is not found
$

Son satır çıktı üretmedi, ortak satır keşfedilmedi.

Şimdi sıralı dosyalar üzerinde comm kullanın , dosyaları işlem ikamesi ile sıralayın:

$ comm <( sort abc ) <( sort def )
123
            132
    321
567
    777
$ comm -12 <( sort abc ) <( sort def )
132

Şimdi 132 hattı var!


2
yani ... sort abc > abc.sorted, sort dev > def.sortedsonra comm -12 abc.sorted def.sorted?
Nikana Reklawyks

1
@NikanaReklawyks Daha sonra geçici dosyaları kaldırmayı ve hata durumunda temizleme işlemiyle başa çıkmayı unutmayın. Birçok senaryoda, işlemin değiştirilmesi çok daha hızlı olacaktır, çünkü sonuçlar belleğe sığdığı sürece disk G / Ç'sini önleyebilirsiniz.
üçlü

29

Perl tek astarını tamamlamak için awkeşdeğeri:

awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2

Bu işlem file1, diziden tüm satırları okur arr[]ve sonra file2dizinin (ör. file1) İçinde zaten var olup olmadığını kontrol eder . Bulunan çizgiler, göründükleri sırayla yazdırılır file2. Karşılaştırmanın dizinden diziye kadar in arrtüm satırı kullandığını file2, bu nedenle yalnızca tüm satırlardaki tam eşleşmeleri rapor edeceğini unutmayın.


2
Bu doğru cevap. Diğerlerinin hiçbiri genel olarak çalışamaz ( perlçünkü bunları denemedim , çünkü). Bir milyon teşekkürler, Bayan
entonio

1
Ortak satırları görüntülerken sırasını korumak, bu nedenle iletişim dışı bırakan bazı durumlarda gerçekten yararlı olabilir.
tuxayo

1
Herkesin aynı şeyi belirli bir sütuna dayanarak yapmak istemesi ancak awk bilmemesi durumunda, her ikisini de $ 0'ları örneğin sütun 5 için 5 $ ile değiştirin, böylece sütun 5'te aynı kelimelerle 2 dosyada paylaşılan satırlar alırsınız
FatihSarigol

24

Belki mi demek istiyorsun comm?

Sıralanan FILE1 ve FILE2 dosyalarını satır satır karşılaştırır.

Seçenek olmadan üç sütunlu çıktı üretin. Birinci sütun FILE1'e özgü satırlar, ikinci sütun FILE2'ye özgü satırlar ve üçüncü sütun her iki dosya için ortak satırlar içerir.

Bu bilgileri bulmanın sırrı bilgi sayfalarıdır. GNU programları için, sayfalarından çok daha ayrıntılıdırlar. Deneyin info coreutilsve size tüm küçük faydalı araçları listeleyecektir.


19

Süre

grep -v -f 1.txt 2.txt > 3.txt

size iki dosya arasındaki farkları verir (2.txt'de ve 1.txt'de değil), kolayca

grep -f 1.txt 2.txt > 3.txt

Sorununuza kolay bir çözüm sunması gereken tüm ortak satırları toplamak için. Dosyalarınızı sıraladıysanız, commyine de almanız gerekir . Saygılarımızla!


2
grepbeklemeyeceğiniz bazı tuhaf şeyler yapar. Özellikle, her şey 1.txtdüz bir dize olarak değil, normal bir ifade olarak yorumlanacaktır. Ayrıca, herhangi bir boş satır içindeki 1.txttüm satırlarla eşleşir 2.txt. Bu sadece çok özel durumlarda işe yarayacaktır.
Christopher Schultz

13
@ChristopherSchultz: Bu cevabı , en modern Unix varyantlarında bulunanlar greptarafından desteklenen POSIX gösterimlerini kullanarak daha iyi çalışacak şekilde yükseltmek mümkündür grep. Düzenli ifadeleri bastırmak için ekleyin -F(veya kullanın fgrep). -xYalnızca tam satırları eşleştirmek için (kesin olarak) ekleyin .
Jonathan Leffler

commSıralanmış dosyaları neden almalıyız ?
Ulysse BN

2
@UlysseBN comm, sıralandığı sürece keyfi olarak büyük dosyalarla çalışabilir, çünkü sadece üç satırda bellek tutması gerekir (tahmin ediyorum ki GNU commsatırlar gerçekten uzunsa sadece bir önek tutmayı bile bilirdi ). grepÇözelti bellekte tüm arama ifadeleri tutması gerekir.
tripleee

9

İki dosya henüz sıralanmamışsa aşağıdakileri kullanabilirsiniz:

comm -12 <(sort a.txt) <(sort b.txt)

ve hata mesajı kaçınarak çalışacaktır comm: file 2 is not in sorted order yaparken comm -12 a.txt b.txt.


Haklısın, ama bu aslında herhangi bir fayda sağlamayan başka bir cevabı tekrarlıyor . İyi kurulmuş ve doğru cevapları olan daha eski bir soruyu cevaplamaya karar verirseniz, günün geç saatlerinde yeni bir yanıt eklemek size herhangi bir kredi getirmeyebilir. Bazı farklı yeni bilgileriniz varsa veya diğer cevapların yanlış olduğuna ikna olduysanız, elbette yeni bir cevap ekleyin, ancak genellikle aynı soruyu soru sorulduktan uzun süre sonra veren 'yine başka bir cevap' kazandı t Çok fazla kredi kazanmazsınız.
Jonathan Leffler

@JonathanLeffler'de bu cevabı bile görmedim, çünkü bu kısım cevabın en sonundaydı, daha önce diğer cevap unsurlarıyla karıştırıldı. Diğer cevap daha kesin olmakla birlikte, hızlı bir çözüm isteyen biri için okumak için sadece 2 satır olacak. Bazen ayrıntılı bir cevap arıyoruz ve bazen acelemiz var ve hızlı bir şekilde okunmaya hazır cevap iyi.
Basj

Ayrıca kredi / temsilcisi umurumda değil, bu amaçla yazı yoktu.
Basj

1
Ayrıca, işlem değiştirme sözdiziminin <(command)POSIX kabuğuna taşınabilir olmadığına, ancak Bash ve bazılarında çalıştığına dikkat edin.
üçlü

8
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

Bundan daha iyi çalışıyor commonu her satırı arar olarak komuta file1içinde file2nerede commçizgi yalnızca karşılaştırır niçinde file1çizgiye eşit niçinde file2.
teriiehina

1
@teriiehina: Hayır; commdosya1'deki N satırını dosya2'deki N satırıyla karşılaştırmaz. Her iki dosyaya da eklenen bir dizi satırı mükemmel bir şekilde yönetebilir (bu, elbette diğer dosyadan bir dizi satırı silmeye eşdeğerdir). Yalnızca girişlerin sıralı olmasını gerektirir.
Jonathan Leffler

commSiparişi korumak istiyorsa cevaplardan daha iyidir . awkBiri kopyalarını istemiyorsa cevap vermekten daha iyidir .
tuxayo



3

Linux'un sınırlı sürümünde (üzerinde çalıştığım bir QNAP (nas) gibi):

  • iletişim yoktu
  • grep -f file1 file2@ChristopherSchultz tarafından söylendiği gibi bazı sorunlara neden olabilir ve grep -F -f file1 file2kullanımı gerçekten yavaştı (5 dakikadan fazla - bitmedi - 20MB üzerindeki dosyalarda aşağıdaki yöntemle 2-3 saniyeden fazla)

İşte yaptığım şey:

sort file1 > file1.sorted
sort file2 > file2.sorted

diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted

files.same.sortedOrijinal ile aynı sırada olacaksa , bu satırı dosya1 ile aynı sipariş için ekleyin:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same

veya dosya2 ile aynı sipariş için:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same

2

Birisi hala birden fazla dosya için bunun nasıl yapılacağını araştırıyorsa, birçok dosyada eşleşen satırları bulma ile ilgili cevaba bakın .


Bu iki yanıtı ( ans1 ve ans2 ) birleştirerek , dosyaları sıralamaksızın ihtiyacınız olan sonucu elde edebileceğinizi düşünüyorum:

#!/bin/bash
ans="matching_lines"

for file1 in *
do 
    for file2 in *
        do 
            if  [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
                echo "Comparing: $file1 $file2 ..." >> $ans
                perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
            fi
         done 
done

Sadece kaydedin, yürütme haklarını verin ( chmod +x compareFiles.sh) ve çalıştırın. Geçerli çalışma dizininde bulunan tüm dosyaları alır ve "matching_lines" dosyasında sonuç bırakarak bir all-vs-all karşılaştırması yapar.

Geliştirilecek şeyler:

  • Dizinleri atla
  • Tüm dosyaları iki kez karşılaştırmaktan kaçının (dosya1'e karşı dosya2 ve dosya2'ye karşı dosya1).
  • Eşleşen dizenin yanına satır numarasını ekleyin

-2
rm file3.txt

cat file1.out | while read line1
do
        cat file2.out | while read line2
        do
                if [[ $line1 == $line2 ]]; then
                        echo $line1 >>file3.out
                fi
        done
done

Bunu yapmalı.


1
Dosyayı silecekseniz muhtemelen kullanmalısınız rm -f file3.txt; dosya yoksa herhangi bir hata bildirmez. OTOH, betiğinizin standart çıktıya yankılanması, betiğin kullanıcısının çıktının nereye gitmesi gerektiğini seçmesine izin vermek gerekli değildir. Sonuçta, muhtemelen sabit dosya adları ( ve ) yerine $1ve $2(komut satırı bağımsız değişkenleri) kullanmak istersiniz . Bu algoritmayı terk eder: yavaş olacaktır. Her satır için bir kez okuyacak . Dosyalar büyükse yavaş olur (birden fazla kilobayt deyin). file1.outfile2.outfile2.outfile1.out
Jonathan Leffler

Kabuk metakarakterleri içermeyen girdileriniz varsa bu nominal olarak çalışabilir (ipucu: shellcheck.net adresinden hangi uyarıları aldığınızı görün ), bu naif yaklaşım son derece verimsizdir. Bir grep -Fdosyayı belleğe okuyan ve sonra diğerinin üzerinden tek bir geçiş yapan bir araç , her iki giriş dosyası üzerinde tekrar tekrar döngüden kaçınır.
üçlü
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.