Periksa apakah semua baris file terjadi di file yang berbeda

14

Saya mendapat dua file: file1 dengan sekitar 10.000 baris dan file2 dengan beberapa ratus baris. Saya ingin memeriksa apakah semua baris file2 terjadi di file1. Yaitu: ∀ baris ℓ ∈ file2: ℓ ∈ file1

Jika ada yang tidak tahu apa arti simbol-simbol ini atau apa yang "memeriksa apakah semua baris file2 terjadi di file1" berarti: Beberapa baris yang setara dalam file mana pun tidak memengaruhi apakah cek mengembalikan apakah file memenuhi persyaratan atau tidak.

Bagaimana saya melakukan ini?

UTF-8
sumber
2
Bolehkah file-file tersebut memiliki garis duplikasi? Jika file2berisi 2 baris A, apakah Anda perlu file1memuat minimal 2 baris A?
Stéphane Chazelas
2
@ StéphaneChazelas Semua baris (di kedua file) dijamin unik.
UTF-8
1
@ UTF-8 Itu akan menjadi detail penting untuk diedit ke pertanyaan Anda.
David Z
2
@ DavidZ Tidak lagi karena jawaban yang ada tidak bergantung pada jaminan itu. Jadi dengan mengedit pertanyaan sekarang, saya akan mengurangi cakupan jawaban yang jelas.
UTF-8
@ UTF-8 Saya kira begitu, walaupun pertanyaannya sedikit ambigu tanpa itu, misalnya jika suatu baris muncul 5 kali dalam file2, apakah garis itu juga harus muncul 5 kali dalam file1 (bukan hanya satu kali)? Jika Anda memiliki persyaratan itu, sepertinya jawaban yang ada tidak akan berfungsi, jadi saya sarankan setidaknya mengedit sesuatu yang membuatnya jelas bahwa bukan itu yang Anda maksudkan.
David Z

Jawaban:

18
comm -13 <(sort -u file_1) <(sort -u file_2)

Perintah ini akan menampilkan baris unik ke file_2. Jadi, jika output kosong, maka semua file_2baris terkandung dalam file_1.

Dari kom pria:

   With  no  options,  produce  three-column  output.  Column one contains
   lines unique to FILE1, column two contains lines unique to  FILE2,  and
   column three contains lines common to both files.

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)
MiniMax
sumber
@don_crissti Benar. Tetap: -uopsi ditambahkan ke sortperintah. Sekarang, hanya baris unik yang tersisa di kedua file yang diurutkan.
MiniMax
Solusi yang sangat sederhana! Apakah sintaks ini berlaku untuk program apa pun yang mengharapkan file? Saya selalu berpikir <pipa menjadi stdin. Apakah istilah braket mengubah ini?
UTF-8
2
@ UTF-8 Ini disebut substitusi proses . Anda dapat membaca di sini tentang hal itu. Dan ya, itu berperilaku seperti file sementara, sehingga dapat digunakan sebagai pengganti file nyata dalam program apa pun, yang mengharapkan file.
MiniMax
Jika ini adalah sesuatu yang sering Anda lakukan, Anda mungkin ingin menyimpan file_1dalam bentuk yang sudah ditentukan. Menghemat pengetikan dan waktu.
Stig Hemmer
7
@minimax Komentar bagus kecuali "any". Substitusi proses, meskipun hebat, tidak dapat digunakan dalam semua kasus, karena "file" yang dihasilkan adalah stream dan bukan file nyata. Ini berarti bahwa mereka tidak "dapat dicari" seperti file normal, dan hanya dapat digunakan ketika program membaca file secara normal dari awal, dan tidak ketika program menggunakan beberapa fungsi file-saja seperti mencari ke titik tertentu atau memutar kembali untuk memulai dari awal. Untungnya, sebagian besar program hanya membaca () file-file mereka, dan karenanya proses substitusi berfungsi dengan sebagian besar program, tetapi bukan program "apa pun".
Law29
7
[ $(grep -cxFf file2 <(sort -u file1)) = $(sort -u file2 | wc -l) ] && 
  echo all there || 
  echo some missing

Jika jumlah kecocokan dari file2 di (baris unik) file1 sama dengan jumlah baris unik di file2, maka semuanya ada di sana; kalau tidak, mereka tidak.

Jeff Schaller
sumber
5

Menggunakan GNU di awkmana ia mendukung length(array)fitur spesifik (dan beberapa awkimplementasi lain yang dapat mendukung) dan tidak diperlukan jika file diurutkan.

gawk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{print (!length(seen))?"Matched":"Not Matched"}' file2 file1

Ini membaca file2 ke dalam array yang disebut seendengan kunci sebagai seluruh baris file2 .

Lalu baca file1 dan untuk setiap baris jika cocok dengan garis dalam array yang terlihat kemudian hapus kunci itu.

Pada akhirnya jika array kosong berarti semua baris dalam file2 ada di file1 dan akan dicetak Matched, jika tidak akan ditampilkanNot Matched .


Untuk kompatibilitas di semua awkimplementasi.

awk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{for(x in seen);print (!x)?"Matched":"Not Matched"}' file2 file1

Untuk mengabaikan baris kosong / atau baris dengan spasi putih hanya jika dalam file2 , Anda perlu menambahkan NFke kondisi di NR==FNR && NF {...untuk melewati membacanya ke dalam array.

αғsнιη
sumber
length(array)adalah AFAIK khusus gawk; pasti bukan POSIX.
dave_thompson_085
@ dave_thompson_085 Benar, saya telah memperbarui jawaban saya. terima kasih
αғsнιη
3

Menggunakan commAnda dapat menemukan garis yang umum di kedua file.

comm -12 file1 file2

Lihat man communtuk lebih jelasnya

Pemburu. Thompson
sumber
Benar itu mengembalikan baris umum di kedua file, tetapi ini tidak memberikan jawaban untuk OP's Q di mana jika Anda memiliki baris di file2 yang tidak keluar di file1, jadi semua baris file2 tidak ada di file1.
αғsнιη
1
file harus diurutkan. Dari man " comm- bandingkan dua file yang disortir baris demi baris".
MiniMax
@MiniMax benar. Ini tidak berhasil. Memanfaatkan jawaban lain commberisi solusi yang jelas tidak salah. Ketika saya menjalankan perintah Anda, saya mendapat peringatan bahwa file tidak dalam urutan dan banyak baris yang pasti ada di kedua file.
UTF-8
3
diff -q <(sort -u file2) <(grep -Fxf file2 file1 | sort -u)

tidak akan menghasilkan keluaran jika file1berisi semua baris masuk file2dan keluar dengan status 0, jika tidak akan mencetak sesuatu seperti

Files /proc/self/fd/11 and /proc/self/fd/12 differ

dan keluar dengan status 1

don_crissti
sumber
2

Gunakan program Python:

#!/usr/bin/env python3
import sys

def open_arg(path):
    return sys.stdin if path == '-' else open(path)

def strip_linebreak(s):
    return s[:-1] if s.endswith('\n') else s

with open_arg(sys.argv[1]) as pattern_file:
    patterns = set(map(strip_linebreak, pattern_file))

with open_arg(sys.argv[2]) as dataset_file:
    for l in map(strip_linebreak, dataset_file):
        patterns.remove(l)
        if not patterns:
            break

sys.exit(int(bool(patterns)))

Pemakaian:

python3 contains-all.py file2 file1

Status keluar program menunjukkan apakah semua pola file 2 cocok:

  • 0 (berhasil) berarti semua pola cocok.
  • 1 (gagal) berarti beberapa pola tidak cocok.

Untuk menanyakan status keluar dalam shell (skrip) Anda dapat menggunakan $?variabel khusus atau ekspresi lain yang mengevaluasi status keluar perintah, mis. Operator hubung singkat &&dan ||dan ekspresi kondisional seperti ifatau while. Contoh:

if python3 compare-all.py file2 file1 && some-other --condition; then
    # do stuff
fi
David Foerster
sumber
1

combinedari moreutils akan menunjukkan kepada Anda semua baris file2yang tidak terkait file1dengan:

combine file2 not file1

Kemudian Anda dapat menghitung jumlah garis dengan memipangnya wc -l, seperti:

if [ $(combine file2 not file1 | wc -l) != 0 ]; then
  echo "lines missing"
else
  echo "You're fine"
fi
Karl Bielefeldt
sumber