Controlla se tutte le righe del file si trovano in file diversi


14

Ho ottenuto due file: file1 con circa 10.000 righe e file2 con poche centinaia di righe. Voglio verificare se tutte le righe di file2 sono presenti in file1. Cioè: ∀ riga ℓ ∈ file2: ℓ ∈ file1

Qualcuno non dovrebbe sapere cosa significano questi simboli o cosa significhi "verificare se tutte le righe del file2 si presentano nel file1": Diverse righe equivalenti in entrambi i file non influenzano se il controllo restituisce che i file soddisfano il requisito o meno.

Come faccio a fare questo?


2
Questi file possono avere righe duplicate? Se file2contiene 2 righe A, devi file1contenere almeno 2 righe A?
Stéphane Chazelas,

2
@ StéphaneChazelas Tutte le linee (in entrambi i file) sono garantite per essere uniche.
UTF-8

1
@ UTF-8 Sarebbe un dettaglio importante da modificare nella tua domanda.
David Z,

2
@DavidZ Non più poiché le risposte esistenti non si basano su tale garanzia. Quindi, modificando la domanda ora, ridurrei la portata apparente delle risposte.
UTF-8

@ UTF-8 Suppongo di sì, anche se la domanda è un po 'ambigua senza di essa, ad esempio se una data riga si presenta 5 volte nel file2, anche quella riga deve ricorrere 5 volte nel file1 (al contrario di una sola volta)? Se avessi questo requisito, non sembra che nessuna delle risposte esistenti funzionerebbe, quindi suggerirei almeno di modificare qualcosa che chiarisca che non è quello che intendi.
David Z,

Risposte:


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

Questo comando produrrà linee uniche per file_2. Quindi, se l'output è vuoto, tutte le file_2righe sono contenute in file_1.

Dall'uomo delle comunicazioni:

   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)

@don_crissti True. Risolto: l' -uopzione aggiunta al sortcomando. Ora, solo le righe univoche vengono lasciate in entrambi i file ordinati.
MiniMax,

Soluzione incredibilmente semplice! Questa sintassi è applicabile a qualsiasi programma che prevede file? Ho sempre pensato che la <pipeline diventasse stdin. Il termine parentesi cambia questo?
UTF-8

2
@ UTF-8 Si chiama Sostituzione processo . Puoi leggere qui al riguardo. E sì, si comporta come un file temporaneo, quindi può essere utilizzato al posto di file reali in qualsiasi programma, il che prevede i file.
MiniMax,

Se si esegue spesso questa operazione, è possibile che si desideri archiviarla file_1in forma preordinata. Risparmia sia la digitazione che il tempo.
Stig Hemmer,

7
@minimax Buon commento tranne il "qualsiasi". La sostituzione del processo, sebbene meravigliosa, non può essere utilizzata in tutti i casi, poiché i "file" risultanti sono flussi e non file reali. Ciò significa che non sono "ricercabili" come un normale file e possono essere utilizzati solo quando il programma legge il file normalmente dall'inizio e non quando il programma utilizza alcune funzionalità di solo file come la ricerca in un punto specifico o riavvolgimento per ricominciare dall'inizio. Fortunatamente, la maggior parte dei programmi legge semplicemente () i loro file, quindi la sostituzione dei processi funziona con la maggior parte dei programmi, ma non con "nessun" programma.
Legge

7
[ $(grep -cxFf file2 <(sort -u file1)) = $(sort -u file2 | wc -l) ] && 
  echo all there || 
  echo some missing

Se il numero di corrispondenze da file2 in (le righe univoche di) file1 è uguale al numero di righe univoche in file2, allora sono tutte lì; altrimenti non lo sono.


5

Usare GNU awkdove supporta length(array)funzionalità specifiche (e qualche altra awkimplementazione che può supportare) e non richiesto se i file sono ordinati.

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

Questo sta leggendo file2 in un array chiamato seencon la chiave come intera riga di file2 .

Quindi leggi file1 e per ogni riga se abbinato a righe nella matrice vista, allora elimina quella chiave.

Alla fine se l'array era vuoto significa che tutte le righe nel file2 esistono nel file1 e verranno stampate Matched, altrimenti verranno visualizzate Not Matched.


Per la compatibilità in tutte le awkimplementazioni.

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

Per ignorare le righe vuote / o le righe con spazi bianchi solo se nel file2 , è necessario aggiungere NFla condizione NR==FNR && NF {...per saltare la lettura nell'array.


length(array)è AFAIK solo per gawk; sicuramente non è POSIX.
dave_thompson_085

@ dave_thompson_085 Esatto, ho aggiornato la mia risposta. grazie
αғsнιη il

3

Usando commpuoi trovare linee comuni in entrambi i file.

comm -12 file1 file2

Dai un'occhiata man commper maggiori dettagli


Corretto sta restituendo le linee comuni in entrambi i file, ma questo non fornisce una risposta alla Q dell'OP, se se avessi una linea nel file2 che non esce nel file1, quindi tutte le linee del file2 non esistono nel file1.
αғsнιη,

1
i file devono essere ordinati. Dall'uomo " comm: confronta due file ordinati riga per riga".
MiniMax,

@MiniMax ha ragione. Questo non funziona L'altra risposta che utilizza commcontiene una soluzione che non è ovviamente errata. Quando eseguo il comando, ricevo avvisi che i file non sono in ordine e molte righe che si trovano sicuramente in entrambi i file.
UTF-8

3
diff -q <(sort -u file2) <(grep -Fxf file2 file1 | sort -u)

non produrrà alcun output se file1contiene tutte le righe dentro file2e termina con lo stato 0, altrimenti stampa qualcosa di simile

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

e uscire con lo stato 1


2

Usa un programma 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)))

Uso:

python3 contains-all.py file2 file1

Lo stato di uscita del programma indica se tutti i pattern del file 2 sono stati trovati:

  • 0 (successo) significa che tutti i modelli sono stati abbinati.
  • 1 (fallimento) significa che alcuni pattern non sono stati abbinati.

Per interrogare lo stato di uscita in un guscio (copione) è possibile utilizzare la $?variabile speciale o altre espressioni che restituiscono exit status dei comandi, ad esempio, gli operatori di corto circuito &&e ||ed espressioni condizionali come ifo while. Esempio:

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

1

combineda moreutils ti mostrerà tutte le linee file2che non sono in file1con:

combine file2 not file1

Quindi puoi contare il numero di linee eseguendo il piping su wc -l, come:

if [ $(combine file2 not file1 | wc -l) != 0 ]; then
  echo "lines missing"
else
  echo "You're fine"
fi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.