Перевірте, чи всі рядки файлу зустрічаються в іншому файлі


14

У мене було два файли: file1 з приблизно 10 000 рядків та file2 з кількома сотнями рядків. Я хочу перевірити, чи всі файли file2 зустрічаються у file1. Тобто: ∀ рядок ℓ ∈ файл2: ℓ ∈ файл1

Якщо хтось не знає, що означають ці символи, або що "перевірити, чи всі рядки файлу2 зустрічаються у file1" означає: Кілька еквівалентних рядків у будь-якому файлі не впливають на те, чи повертається чек, що файли відповідають вимозі, чи ні.

Як це зробити?


2
Чи можуть ці файли дублювати рядки? Якщо file2містить 2 рядки A, чи потрібно file1містити принаймні 2 рядки A?
Stéphane Chazelas

2
@ StéphaneChazelas Усі рядки (в обох файлах) гарантовано унікальні.
UTF-8

1
@ UTF-8 Це буде важливою деталлю для редагування вашого запитання.
David Z

2
@DavidZ Більше, оскільки існуючі відповіді не покладаються на цю гарантію. Отже, редагуючи питання зараз, я би зменшив уявний обсяг відповідей.
UTF-8

@ UTF-8 Я думаю, що так, хоча питання без нього є неоднозначним, наприклад, якщо даний файл в файлі2 трапляється 5 разів, чи цей рядок також повинен виникати 5 разів у file1 (на відміну від лише одного разу)? Якби у вас була ця вимога, схоже, що жоден із існуючих відповідей не спрацював би, тому я б запропонував принаймні редагувати те, що дає зрозуміти, що це не так.
David Z

Відповіді:


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

Ця команда виведе рядки, унікальні для file_2. Отже, якщо вихід порожній, то всі file_2рядки містяться у file_1.

Від людини кому:

   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 Правда. Виправлено: -uопція додана до sortкоманди. Тепер в обох відсортованих файлах залишаються лише унікальні рядки.
MiniMax

Дивовижне просто рішення! Чи застосовується цей синтаксис до будь-якої програми, яка очікує файлів? Я завжди думав, що <труба в stdin. Чи змінюється дужка термін це?
UTF-8

2
@ UTF-8 Це називається Процес заміщення . Ви можете прочитати тут про це. І так, він поводиться як тимчасовий файл, тому його можна використовувати замість реальних файлів у будь-яких програмах, які очікують файли.
MiniMax

Якщо це ви робите часто, можливо, вам захочеться зберігати їх file_1у зібраному вигляді. Економить як набравши текст, так і час.
Стиг Хеммер

7
@minimax Хороший коментар за винятком "будь-якого". Заміна процесу, хоча і чудова, не може бути використана у всіх випадках, оскільки отримані "файли" - це потоки, а не реальні файли. Це означає, що вони не "шукаються", як звичайний файл, і їх можна використовувати лише тоді, коли програма читає файл нормально з самого початку, а не тоді, коли програма використовує певні функції, такі як пошук до певної точки або перемотування, щоб почати з початку. На щастя, більшість програм просто читають () свої файли, і тому підміна процесів працює з більшістю програм, але не з "будь-якими" програмами.
Закон29

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

Якщо кількість збігів з file2 в (унікальних рядках) file1 є такою ж, як кількість унікальних рядків у file2, то вони всі є; інакше вони не є.


5

Використання GNU awkтам, де він підтримує специфічну length(array)функцію (та якусь іншу awkреалізацію, яка може підтримувати) і не потрібна, якщо файли сортуються.

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

Це зчитування файлу2 в масив, викликаний seenключем, як весь рядок file2 .

Потім прочитайте файл1 і для кожного рядка, якщо він збігається з рядками в баченому масиві, а потім видаліть цей ключ.

Зрештою, якщо масив був порожнім означає, що всі рядки у file2 існують у file1 та будуть надруковані Matched, інакше відобразяться Not Matched.


Для сумісності у всіх awkреалізаціях.

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

Щоб ігнорувати порожні рядки / або рядки з пробілами лише у файлі2 , вам потрібно буде додати NFумову, NR==FNR && NF {...щоб пропустити їх читання в масив.


length(array)є AFAIK лише для гоуку; це точно не POSIX.
dave_thompson_085

@ dave_thompson_085 Правильно, я оновив свою відповідь. дякую
αғsnιη

3

Використовуючи commви можете знайти рядки, які є загальними для обох файлів.

comm -12 file1 file2

Подивіться man commдетальніше


Виправте це повернення загальних рядків в обох файлах, але це не дає відповіді на питання ОП, де, якби у вас була лінія у file2, яка не виходить у файл1, тому всі рядки file2 не існують у file1.
αғsnιη

1
Файли повинні бути відсортовані. Від людини " comm- порівняйте два відсортовані файли по рядку".
MiniMax

@MiniMax має рацію. Це не працює. Інша відповідь commмістить рішення, яке, очевидно, невірно. Коли я запускаю вашу команду, я отримую попередження про те, що файли не впорядковані і багато рядків, які безумовно є в обох файлах.
UTF-8

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

не дасть результату, якщо file1містить всі рядки у file2та виходить зі статусом 0, інакше він надрукує щось подібне

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

і вийти зі статусом 1


2

Використовуйте програму 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)))

Використання:

python3 contains-all.py file2 file1

Статус виходу з програми вказує, чи були узгоджені всі шаблони файлу 2:

  • 0 (успіх) означає, що всі шаблони були узгоджені.
  • 1 (поломка) означає, що деякі зразки не відповідали.

Для запиту стану виходу в оболонці (сценарій) , ви можете використовувати або $?спеціальні змінні або інші вирази , які оцінюють стан виходу команди, наприклад , оператор від короткого замикання &&і ||і умовних вираження , як ifабо while. Приклад:

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

1

combineвід moreutils покаже вам всі рядки, у file2яких немає file1:

combine file2 not file1

Тоді ви можете порахувати кількість рядків, переклавши його wc -l, наприклад:

if [ $(combine file2 not file1 | wc -l) != 0 ]; then
  echo "lines missing"
else
  echo "You're fine"
fi
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.