Linux terminalindeki iki dosyayı karşılaştırma


168

Olarak adlandırılan iki dosya var "a.txt" ve "b.txt" kelime listesi var hem. Şimdi ekstra hangi kelimeleri kontrol etmek istiyorum "a.txt" ve olmayan "b.txt" .

İki sözlük karşılaştırmam gerektiğinden verimli bir algoritmaya ihtiyacım var.


28
diff a.txt b.txtyeterli değil?
13

Sözcükler her dosyada birkaç kez ortaya çıkabilir mi? Dosyaları sıralayabilir misiniz?
Basile Starynkevitch

sadece "b.txt" 'de mevcut olmayan ve a.txt' de mevcut olan kelimelere ihtiyacım var
Ali Imran

Yanıtlar:


343

vim yüklüyse şunu deneyin:

vimdiff file1 file2

veya

vim -d file1 file2

fantastik bulacaksınız.resim açıklamasını buraya girin


9
kesinlikle harika, iyi tasarım ve farkları bulmak kolay. Ohmygod
Zen

1
Cevabınız harika, ama öğretmenim herhangi bir kütüphane fonksiyonunu kullanmamı istedi: P
Ali Imran

1
Ne harika bir araç! Bu son derece yararlı.
user1205577

1
Bu renklerin anlamları nedir?
zygimantus

1
Renkli kodlar, iki dosyada farklı oldukları anlamına gelir. @zygimantus
Fengya Li

73

Bunları sıralayın ve kullanın comm:

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

commgirdi dosyalarını karşılaştırır (sıralar) ve varsayılan olarak üç sütun çıkarır: a'ya özgü satırlar, b'ye özgü satırlar ve her ikisinde bulunan satırlar. Belirterek -1, -2ve / veya -3karşılık gelen çıktıyı bastırmak olabilir. Bu nedenle comm -23 a b, yalnızca a'ya özgü girdileri listeler. <(...)Sözdizimini dosyaları anında sıralamak için kullanıyorum, zaten sıralanmışlarsa buna ihtiyacınız yok.


Sadece grep komutlarını kullanarak kendi cevabımı ekledim, lütfen bana daha etkili olduğunu söyle?
Ali Imran

3
@AliImran, commdaha verimli çünkü işi tüm dosyayı hafızaya kaydetmeden tek bir seferde yapıyor. Büyük olasılıkla zaten sıralanmış olan sözlükler kullandığınız için sortbunlara bile ihtiyacınız yoktur . Kullanımı grep -f file1 file2diğer taraftan bütününü yükleyecektir file1belleğe ve her satırı karşılaştırmak file2çok daha az randımanlı olduğu girişler, hepsi ile. Çoğunlukla küçük, sıralanmamış için yararlıdır -f file1.
Anders Johansson

1
@AndersJohansson "comm" komutunu paylaştığın için teşekkürler. Gerçekten de şık. Sık sık dosyalar arasında dış birleşimler yapmak zorunda ve bu hile yapar.
blispr

Yeni çizgi karakterine dikkat edin ... Karşılaştırmaya \nda dahil edileceğini buldum .
Bin


28

diffLinux'ta iki dosyayı karşılaştırmak için aracı kullanabilirsiniz . Gerekli verileri filtrelemek için --changed-group-format ve --unchanged-group-format seçeneklerini kullanabilirsiniz.

Her seçenek için ilgili grubu seçmek üzere aşağıdaki üç seçenek kullanılabilir:

  • '% <' FILE1'den satır al

  • '%>' FILE2'den satır al

  • Her iki dosyadan da satırları kaldırmak için '' (boş dize).

Örn: diff --changed-group-format = "% <" --unchanged-group-format = "" dosya1.txt dosyası2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

Fark çıktı stilini tercih git diffederseniz, --no-indexgit deposunda olmayan dosyaları karşılaştırmak için bayrakla kullanabilirsiniz:

git diff --no-index a.txt b.txt

Her birinde yaklaşık 200 bin dosya adı dizesi bulunan birkaç dosya kullanarak, timebu yaklaşımı (diğer yerleşik komutla) karşılaştırdım ve buradaki diğer yanıtlardan bazıları:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commfarkla en hızlı çıktı gibi gözükse de, git diff --no-indexfark tarzı çıktı için en hızlı yaklaşım gibi görünmektedir.


Güncelleme 2018-03-25--no-index Bir git deposunun içinde değilseniz ve bu depodaki izlenmemiş dosyaları karşılaştırmak istemiyorsanız bayrağı atlayabilirsiniz . Gönderen man sayfaları :

Bu form, dosya sisteminde verilen iki yolu karşılaştırmak içindir. Komut Git tarafından kontrol edilen bir çalışma ağacında ve çalışma ağaçlarının dışındaki yol noktalarından en az birini çalıştırırken veya Git tarafından kontrol edilen bir çalışma ağacının dışında komutu çalıştırırken --no-index seçeneğini atlayabilirsiniz.




4

Kullan comm -13 (sıralanmış dosyalar gerektirir) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

İşte bunun için benim çözüm:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
Diğer çözümlerden herhangi birini denediniz mi? Bu çözümlerden biri sizin için yararlı oldu mu? Sorunuz birçok kullanıcıda çizilebilecek kadar genel, ancak cevabınız benim zevkime daha spesifik ... Çünkü benim özel durumum sdiff -s file1 file2faydalı oldu.
Metafaniel

@Metafaniel benim çözüm sdiff komutunu kullanmayın. Sorunu çözmek için yalnızca linux yerleşik komutlarını kullanır.
Ali Imran

-1

Bunun için awk kullanma. Test dosyaları:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

Awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

Kopyalar çıktı olarak verilir:

four
four

Yinelemeleri önlemek için, yeni bir araya getirilen her kelimeyi seenkarma olarak a.txt dosyasına ekleyin :

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

Çıktı:

four

Kelime listeleri virgülle ayrılmışsa, örneğin:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

birkaç ekstra tur ( fordöngüler) yapmanız gerekiyor :

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

Bu seferki çıktı:

four
five,six
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.