İki dosyayı satır satır karşılaştırın ve farkı başka bir dosyada oluşturun


121

Dosya1 ile dosya2'yi karşılaştırmak ve dosya1'de dosya2'de bulunmayan satırları içeren bir dosya3 oluşturmak istiyorum.


Dif denedim ama farklı satırların önünde bazı sayılar ve başka semboller oluşturuyor ve bu da dosyaları karşılaştırmamı zorlaştırıyor.
Paz

Yanıtlar:


216

diff (1) cevap değil, ama comm (1) cevap.

NAME
       comm - compare two sorted files line by line

SYNOPSIS
       comm [OPTION]... FILE1 FILE2

...

       -1     suppress lines unique to FILE1

       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

Yani

comm -2 -3 file1 file2 > file3

Girdi dosyaları sıralanmalıdır. Değilse, önce sıralayın. Bu geçici bir dosya ile yapılabilir veya ...

comm -2 -3 <(sort file1) <(sort file2) > file3

kabuğunuzun süreç ikamesini desteklemesi koşuluyla (bash yapar).


1
İki dosyanın sıralanması gerektiğini ve benzersiz olduğunu unutmayın
andy

6
Seçenekleri birlikte gruplayabilirsiniz:comm -23
Paolo M

"Sıralanmış" ne anlama geliyor? Hatların aynı sıraya sahip olduğunu mu? Öyleyse, çoğu kullanım durumu için büyük olasılıkla iyidir - olduğu gibi, yedeklenmiş eski bir sürümle karşılaştırarak hangi satırların eklendiğini kontrol edin. Yeni eklenen satırlar mevcut satırlar arasında olamazsa, bu daha çok bir sorundur.
Egor Hans

@EgorHans: Eğer dosyada örneğin "3 \ n1 \ n3 \ n2 \ n" gibi tamsayılar içeren satırlar varsa, önce artan veya azalan sırayla yeniden sıralanmalıdır, örneğin "\ 1 \ n2 \ n3 \ n3 \ n" kopyalar ile bitişik. Bu "sıralanır" ve her iki dosya da benzer şekilde sıralanmalıdır. Daha yeni dosyanın yeni satırları olduğunda, bunların "mevcut satırlar arasında" olup olmadıklarının önemi yoktur çünkü sıralamadan sonra sıralanmamışlardır.
sorpigal

49

Unix yardımcı programı difftam olarak bu amaç içindir.

$ diff -u file1 file2 > file3

Seçenekler, farklı çıktı biçimleri vb. İçin kılavuza ve İnternet'e bakın.


8
Bu istenen işi yapmaz; diğer yanıtlarda önerilen komut satırı anahtarlarının kullanımıyla bile bir sürü ekstra karakter ekler.
xenocyon

20


Şunu düşünün: a.txt dosyası:

abcd
efgh

b.txt dosyası:

abcd

Farkı şununla bulabilirsiniz:

diff -a --suppress-common-lines -y a.txt b.txt

Çıktı şu şekilde olacaktır:

efgh 

Aşağıdakileri kullanarak çıktıyı bir çıktı dosyasında (c.txt) yeniden yönlendirebilirsiniz:

diff -a --suppress-common-lines -y a.txt b.txt > c.txt

Bu, sorunuza cevap verecektir:

"... dosya1'deki dosya2'de bulunmayan satırları içeren."


2
Bu yanıtın iki sınırlaması vardır: (1) yalnızca kısa satırlar için çalışır (varsayılan olarak 80 karakterden az, ancak bu değiştirilebilir olsa da) ve daha da önemlisi, (2) her birinin sonuna bir "<" ekler başka bir programla kaldırılması gereken satır (örn. awk, sed).
29'da sergut

Çoğu durumda, mümkün olan en küçük farkı bulmak için elinden gelenin en iyisini -dyapacak olan, kullanmak isteyeceksiniz diff. -i, -E, -w, -BVe --suppress-blank-emptyayrıca her ne kadar her zaman değil, bazen yararlı olabilir. Kullanım durumunuza neyin uyduğunu bilmiyorsanız, diff --helpönce deneyin (bu genellikle bir komutun ne yapabileceğini bilmediğinizde iyi bir fikirdir).
Egor Hans

Ayrıca, --line-format =% L kullanarak, fazladan karakter üretmekten kaçınırsınız (en azından, yardım bu şekilde çalıştığını söylüyor, ancak yine de denemek üzeredir).
Egor Hans

Ayrıca bu daha kısa ve aynı stackoverflow.com/a/27667185/1179925
mrgloom

8

Bazen diffihtiyacınız olan yardımcı programdır, ancak bazen joindaha uygundur. Dosyaların önceden sıralanması gerekir veya bash, ksh veya zsh gibi işlem değiştirmeyi destekleyen bir kabuk kullanıyorsanız, sıralamayı anında yapabilirsiniz.

join -v 1 <(sort file1) <(sort file2)

Bunun için madalya almalısın! Son 2 saatte tam olarak aradığım şey buydu
Zatarra

7

Deneyin

sdiff file1 file2

Genelde benim için çoğu durumda çok daha iyi çalışıyor. Satır sırası önemli değilse (örneğin bazı metin yapılandırma dosyaları) dosyaları önceden sıralamak isteyebilirsiniz.

Örneğin,

sdiff -w 185 file1.cfg file2.cfg

1
Güzel hizmet! Farklılaştıran çizgileri nasıl işaretlediğini seviyorum. Yapılandırmaları karşılaştırmayı çok daha kolay hale getirir. Bu, sıralama ile birlikte ölümcül bir kombinasyondur (örneğin sdiff <(sort file1) <(sort file2))
jmagnusson

3

Bunu coreutils ile çözmeniz gerekiyorsa, kabul edilen cevap iyidir:

comm -23 <(sort file1) <(sort file2) > file3

Ayrıca sıralama veya işlem ikamesi gerektirmeyen ve sonsuz akışları destekleyen sd (stream diff) de kullanabilirsiniz , örneğin:

cat file1 | sd 'cat file2' > file3

Muhtemelen bu örnek için pek bir fayda sağlamaz, ancak yine de düşünün; bazı durumlarda commne grep -Fde kullanamazsınız diff.

Burada , terminalde farklı akışlar hakkında yazdığım, sd'yi tanıtan bir blog yazısı var.


3

Yine de grepçözüm yok mu?

  • sadece dosya2'de bulunan satırlar:

    grep -Fxvf file1 file2 > file3
  • sadece dosya1'de bulunan satırlar:

    grep -Fxvf file2 file1 > file3
  • her iki dosyada bulunan satırlar:

    grep -Fxf file1 file2 > file3

2

Zaten birçok cevap var, ancak hiçbiri mükemmel IMHO. Thanatos'un cevabı her satırda fazladan karakter bırakır ve Sorpigal'ın cevabı dosyaların sıralanmasını veya önceden sıralanmasını gerektirir ki bu her koşulda yeterli olmayabilir.

Farklı ve başka bir şey (hiçbir ekstra karakter, hiçbir yeniden sipariş) olan hatları almanın en iyi yolu bir kombinasyonudur düşünüyorum diff, grepve awk(veya benzeri).

Satırlar "<" içermiyorsa, kısa bir tek satırlık olabilir:

diff urls.txt* | grep "<" | sed 's/< //g'

ancak bu her "<" örneğini (küçüktür, boşluk) satırlardan kaldıracaktır, bu her zaman uygun değildir (örneğin kaynak kodu). En güvenli seçenek awk kullanmaktır:

diff urls.txt* | grep "<" | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'

Bu tek satırlık, her iki dosyayı da farklılaştırır, daha sonra diff'in ed stili çıktısını filtreler ve ardından diff'in eklediği sondaki "<" öğesini kaldırır. Bu, satırlar bazı "<" içerse bile çalışır.


1
comm sıralama gerektirmez (daha yeni sürümlerde?) - sadece --nocheck-order kullanın. Bunu
CLI'den csv'leri işlerken

2

Sözünü sürpriz hiçim diff -yiçin bir yan-yana çıktı üretir , örneğin:

diff -y file1 file2 > file3

Ve içinde file3(farklı satırların |ortasında bir sembol vardır):

same     same
diff_1 | diff_2

1

Diff yardımcı programını kullanın ve çıktıda yalnızca <ile başlayan satırları ayıklayın


0
diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt

Bu konudaki neredeyse tüm cevapları denedim ama hiçbiri tamamlanmadı. Yukarıdaki birkaç patikadan sonra benim için çalıştı. diff size fark verecektir ama bazı istenmeyen özel karakterlerle. gerçek fark çizgileri '>' ile başlar. bu yüzden bir sonraki adım grep satırlarının '>' ile başlaması ve ardından sed ile aynısının kaldırılması .


1
Bu kötü bir fikir. Ayrıca ile başlayan satırları da değiştirmeniz gerekir <. Girdi dosyalarının sırasını değiştirirseniz bunu göreceksiniz. Bunu yapsanız bile grepdaha fazla sed kullanarak atlamak isteyeceksiniz : `diff a1 a2 | sed '/> / s ///' `` Bu yine de içeren >veya <doğru durumda olan satırları kesebilir ve yine de satır numaralarını açıklayan ekstra satırlar bırakır. Bu yaklaşımı denemek istiyorsa daha iyi bir yolu olacaktır: diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'.
sorpigal

0

diffAşağıdaki çıktı biçimlendirmesiyle kullanabilirsiniz :

diff --old-line-format='' --unchanged-line-format='' file1 file2

--old-line-format='', dosya2'de satır farklıysa dosya1 için çıktıyı devre dışı bırakın.
--unchanged-line-format='', satırlar aynıysa çıktıyı devre dışı bırakın.


0

Tek veya çok sütunlu bir CSV dosyanız varsa, bu satır satır "diff" işlemlerini sqlite3 gömülü db kullanarak yapabilirsiniz. Python ile birlikte gelir, bu nedenle çoğu linux / mac'ta mevcut olmalıdır. Python yazmaya gerek kalmadan bash kabuğundaki sqlite3 komutlarını yazabilirsiniz.

  1. A.csv ve b.csv dosyalarınızı oluşturun
  2. Sqlite3'ün "sqlite3 -help" komutunu kullanarak kurulduğundan emin olun
  3. Aşağıdaki komutları doğrudan Linux / Mac kabuğunda çalıştırın (veya bir komut dosyasına yerleştirin)
echo "
.mode csv
.import a.csv atable
.import b.csv btable
create table result as select * from atable EXCEPT select * from btable;
.output result.csv
select * from result ;
.quit
" | sqlite3 temp.db

Not: sqlite3 komutlarının her biri için bir satırsonu olduğundan emin olun .

Nasıl çalışır

  1. 2 csv'yi sırasıyla "atable" ve "btable" a aktarın.
  2. "Atable" da bulunan ancak "btable" da eksik olan verileri seçmek için " dışında " sql operatörünü kullanın. Select sorgu deyimini kullanarak bir "sonuç" tablosu oluşturun
  3. "Select * from result;" komutunu çalıştırarak sonuç tablosunu result.csv'ye çıkarın

Belirli sütunlar üzerinde işlem yapmanız gerekiyorsa, sqlite3 veya herhangi bir db gitmenin yoludur.

Yerleşik fark ve iletişim araçlarını kullanarak birden çok GB dosyasında farklılaşmayı denedim. Sqlite, linux hizmetlerini bir mil geride bırakır.

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.