B dosyasında görünen satırları başka bir A dosyasından nasıl kaldırabilirim?


160

Her e-posta için bir satır (e-postalardan oluşan) büyük bir dosya var . Ayrıca başka bir posta kümesi içeren başka bir dosya B var.

B dosyasında görünen tüm adresleri A dosyasından kaldırmak için hangi komutu kullanacağım.

Yani, A dosyası içeriyorsa:

A
B
C

ve B dosyası şunları içeriyordu:

B    
D
E

Sonra A dosyası şu şekilde bırakılmalıdır:

A
C

Şimdi bunun daha sık sorulan bir soru olduğunu biliyorum, ancak kötü bir sınırlayıcıyla ilgili bir hata veren çevrimiçi tek bir komut buldum .

Herhangi bir yardım çok takdir edilecektir! Birisi kesinlikle zeki bir astarla gelecektir, ama ben kabuk uzmanı değilim.



1
Buradaki yanıtlar sıralı dosyalar içinse ve en bariz olanı eksikse, ki bu elbette sizin hatanız değil, ancak diğerini daha genel olarak kullanışlı kılar.
üçlü

Yanıtlar:


204

Dosyalar sıralanıyorsa (bunlar örneğinizdedir):

comm -23 file1 file2

-23her iki dosyadaki veya yalnızca dosya 2'deki satırları bastırır. Dosyalar sıralanmamışsa, sortönce bunları borulara ekleyin ...

Burada man sayfasına bakın


8
comm -23 file1 file2 > file3dosya2'deki dosya2'deki içeriği dosya3'e çıktılar. Ve mv file3 file1nihayet dosya1'deki gereksiz içerikleri temizler.
Spectral

2
Alternatif olarak, kullanın comm -23 file1 file2 | sponge file1. Temizlik gerekmez.
Socowi

Man page link benim için yüklenmiyor
Felix Rabe

@Socowi Sünger nedir? Benim sistemimde bu yok. (macos 10.13)
Felix Rabe

@ FelixRabe, bu çok yorucu. Bağlantınızla değiştirildi. Teşekkürler
Arketipik Paul

85

grep -Fvxf <lines-to-remove> <all-lines>

  • sıralanmamış dosyalar üzerinde çalışır
  • siparişi korur
  • POSIX

Misal:

cat <<EOF > A
b
1
a
0
01
b
1
EOF

cat <<EOF > B
0
1
EOF

grep -Fvxf B A

Çıktı:

b
a
01
b

Açıklama:

  • -F: varsayılan BRE yerine değişmez dizeler kullan
  • -x: yalnızca tüm çizgiyle eşleşen eşleşmeleri düşünün
  • -v: eşleşmeyen yazdır
  • -f file: verilen dosyadan desen al

Bu yöntem, önceden sıralanan dosyalar üzerinde diğer yöntemlere göre daha yavaştır, çünkü daha geneldir. Hız da önemliyse, bkz: Bir dosyada başka bir dosyada olmayan satırları bulmanın hızlı yolu?

Hat içi çalışma için hızlı bir bash otomasyonu:

remove-lines() (
  remove_lines="$1"
  all_lines="$2"
  tmp_file="$(mktemp)"
  grep -Fvxf "$remove_lines" "$all_lines" > "$tmp_file"
  mv "$tmp_file" "$all_lines"
)

GitHub akış yukarı .

kullanımı:

remove-lines lines-to-remove remove-from-this-file

Ayrıca bkz: /unix/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in-another


55

kurtarmaya awk!

Bu çözüm sıralı girişler gerektirmez. Önce fileB'yi sağlamanız gerekir.

awk 'NR==FNR{a[$0];next} !($0 in a)' fileB fileA

İadeler

A
C

O nasıl çalışır?

NR==FNR{a[$0];next} deyim, daha sonraki bir "içerir" testinin anahtarları olarak ilk dosyayı ilişkilendirilebilir bir dizide saklamak içindir.

NR==FNR genel satır sayacının (NR) geçerli dosya satırı sayacına (FNR) eşit olduğu ilk dosyayı taradığımızı kontrol ediyor.

a[$0] geçerli satırı ilişkilendirilebilir diziye anahtar olarak ekler, bunun yinelenen değerlerin (anahtarlar) olmayacağı bir küme gibi davrandığını unutmayın

!($0 in a)şimdi bir sonraki dosya (lar ) indayız, bir içerme testi, burada mevcut satırın ilk dosyadan ilk adımda doldurduğumuz sette olup olmadığını kontrol ediyor !, koşulu reddediyor. Burada eksik olan, varsayılan olarak {print}açıkça yazılan ve genellikle yazılmayan eylemdir .

Bunun artık kara listeye alınan kelimeleri kaldırmak için kullanılabileceğini unutmayın.

$ awk '...' badwords allwords > goodwords

küçük bir değişiklikle birden fazla listeyi temizleyebilir ve temizlenmiş sürümler oluşturabilir.

$ awk 'NR==FNR{a[$0];next} !($0 in a){print > FILENAME".clean"}' bad file1 file2 file3 ...

bu konuda tam not. Bunu Windows'ta GnuWin32'deki komut satırında kullanmak için tek nibble'ları çift tırnak işareti ile değiştirin. bir tedavi çalışır. çok teşekkürler.
twobob

Bu işe yarar ama çıktıyı A (A ile yeni bir çizgi) şeklinde fileA'ya nasıl yönlendirebilirim
Anand Builders

Sanırım A\nC, önce geçici bir dosyaya yazın ve orijinal dosyanın üzerine yazın... > tmp && mv tmp fileA
karakfa

Bunda da tam not var. Bu awk, 104.000 girişli bir dosyayı işlemek için 1 saniyenin tamamını alır: +1:
MitchellK

Bunu komut dosyalarında kullanırken, önce fileBboş olmadığını (0 bayt uzunluğunda) kontrol ettiğinizden emin olun , çünkü öyleyse, beklenen içeriği yerine boş bir sonuç alırsınız fileA. (Neden: FNR==NRo zaman geçerli olacak fileA.)
Peter Nowee


7

Dosyalarınız sıralanmadıkça bunu yapabilirsiniz

diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a

--new-line-formatb dosyasındaki satırlar içindir, ancak a'da olmayan --old-..satırlar içindir. a dosyasındaki satırlar içindir, ancak b değil --unchanged-.., her iki satırdaki satırlar içindir. %Lçizginin tam olarak yazdırılmasını sağlar.

man diff

daha fazla ayrıntı için


1
Dosyalar sıralanmadıkça bunun işe yarayacağını söylersiniz. Sıralanırlarsa hangi sorunlar oluşur? Kısmen sıralanırlarsa ne olur?
Carlos Macasaet

1
Bu, commkomutun önerilen kullanımını önerdi . commdosyaların sıralanmasını gerektirir, bu nedenle sıralandıysa bu çözümü de kullanabilirsiniz. Dosyanın sıralanıp sıralanmadığına bakılmaksızın bu çözümü kullanabilirsiniz
aec

7

@ Karakfa'nın güzel cevabının bu hassaslaştırılması çok büyük dosyalar için fark edilir derecede daha hızlı olabilir. Bu yanıtta olduğu gibi, her iki dosyanın da sıralanması gerekmez, ancak awk'ın ilişkilendirilebilir dizileri sayesinde hız sağlanır. Yalnızca arama dosyası bellekte tutulur.

Bu formülasyon aynı zamanda, karşılaştırma dosyasında giriş dosyasındaki sadece bir alanın ($ N) kullanılmasına olanak tanır.

# Print lines in the input unless the value in column $N
# appears in a lookup file, $LOOKUP;
# if $N is 0, then the entire line is used for comparison.

awk -v N=$N -v lookup="$LOOKUP" '
  BEGIN { while ( getline < lookup ) { dictionary[$0]=$0 } }
  !($N in dictionary) {print}'

(Bu yaklaşımın bir başka avantajı, karşılaştırma ölçütünü değiştirmenin, örneğin önde gelen ve arkadaki beyaz boşluğu düzeltmenin kolay olmasıdır.)


Bu, bir diğer çapraz platform senaryosunda diğer bir astardan daha zordur. Ancak performans çabası için şapka kapalı
twobob

2

Python'u kullanabilirsiniz:

python -c '
lines_to_remove = set()
with open("file B", "r") as f:
    for line in f.readlines():
        lines_to_remove.add(line.strip())

with open("file A", "r") as f:
    for line in [line.strip() for line in f.readlines()]:
        if line not in lines_to_remove:
            print(line)
'

2

Kullanabilirsiniz - diff fileA fileB | grep "^>" | cut -c3- > fileA

Bu, sıralanmamış dosyalar için de çalışır.


-1

İki dosya arasındaki ortak satırları kaldırmak için grep, comm veya join komutunu kullanabilirsiniz.

grep yalnızca küçük dosyalar için çalışır. -F ile birlikte -v kullanın.

grep -vf file2 file1 

Bu, dosya1'deki dosya2'deki hiçbir satırla eşleşmeyen satırları görüntüler.

comm, sözcüklere göre sıralanmış dosyalar üzerinde çalışan bir yardımcı program komutudur. İki dosyayı girdi olarak alır ve çıktı olarak üç metin sütunu üretir: yalnızca ilk dosyadaki satırlar; yalnızca ikinci dosyadaki satırlar; ve her iki dosyadaki çizgiler. Buna göre -1, -2 veya -3 seçeneğini kullanarak herhangi bir sütunun yazdırılmasını engelleyebilirsiniz.

comm -1 -3 file2 file1

Bu, dosya1'deki dosya2'deki hiçbir satırla eşleşmeyen satırları görüntüler.

Son olarak, belirtilen dosyalarda eşitlik birleştirme gerçekleştiren bir yardımcı program komutu olan join vardır. -V seçeneği, iki dosya arasındaki ortak satırların kaldırılmasına da izin verir.

join -v1 -v2 file1 file2

Bunların hepsi zaten diğer cevaplarda verilmiştir. Grep'inizin bir -F'ye ihtiyacı var, ya da çizgiler regexps gibi göründüğünde garip sonuçlar alacaksınız
Archetypal Paul
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.