Есть ли инструмент для получения строк в одном файле, которые не находятся в другом?


110

Есть ли какой-нибудь инструмент, который может получить строки, которые содержит файл A, а файл B - нет? Я мог бы сделать небольшой простой скрипт, например, с Perl, но если что-то подобное уже существует, я буду экономить свое время с этого момента.



Ответы:


159

Да. Стандартный grepинструмент для поиска файлов по текстовым строкам можно использовать для вычитания всех строк в одном файле из другого.

grep -F -x -v -f fileB fileA

Это работает, используя каждую строку в fileB как pattern ( -f fileB) и обрабатывая ее как простую строку для соответствия (не регулярное регулярное выражение) ( -F). Вы заставляете совпадение происходить по всей строке ( -x) и выводите только те строки, которые не совпадают ( -v). Поэтому вы печатаете строки в файле A, которые не содержат те же данные, что и строки в файле B.

Недостатком этого решения является то, что оно не учитывает порядок строк, и если ваш ввод содержит повторяющиеся строки в разных местах, вы можете не получить то, что ожидаете. Решением этой проблемы является использование реального инструмента сравнения, такого как diff. Это можно сделать, создав файл diff со значением контекста в 100% строк в файле, а затем проанализировав его только для строк, которые будут удалены при преобразовании файла A в файл B. (Обратите внимание, что эта команда также удаляет diff форматирование после того, как он получает правильные строки.)

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC

@ inderpreet99 Аргумент в нижнем регистре -uдействительно принимает параметр числа, если за ним не стоит пробел. Преимущество того, что у меня было раньше, заключается в том, что оно будет работать со значением или без него, так что вы можете использовать что-то в подпрограмме подкоманды, которая не выдает результат. В верхнем регистре '-U', с другой стороны, требуется аргумент.
Калеб

будьте осторожны, grep -f = O (N ^ 2) Я считаю: stackoverflow.com/questions/4780203/…
rogerdpack

1
diffтрубопровод работает лакомство спасибо.
Фелипе Альварес

Чтобы учесть проблему сортировки, вы можете использовать подстановку процесса в команде, чтобы обработать каждый файл перед тем, grepкак необходимо. Пример:grep -F -x -v -f <(sort fileB) <(sort fileA)
Тони Чезаро

@TonyCesaro Это сработает, если ваш набор данных не относится к конкретному заказу, и дубликаты не нужно принимать во внимание. Преимущество использования diffзаключается в том, что позиция в файле учитывается.
Калеб

57

Ответ во многом зависит от типа и формата файлов, которые вы сравниваете.

Если сравниваемые файлы являются отсортированными текстовыми файлами, то инструмент GNU, написанный Ричардом Столлманом и Дэвидом Маккензи, commможет выполнить фильтрацию, к которой вы стремитесь. Это часть coreutils.

пример

Допустим, у вас есть следующие 2 файла:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Строки в файле b, которых нет в файле a:

$ comm <(sort a) <(sort b) -3
    6

1
+1 за упоминание comm; к сожалению, commтребуются отсортированные файлы
Arcege

11
так их сортировать? comm <(sort a) <(sort b) -1 -2
Sirex

Это какой-то странный синтаксис. <()? Это работает, и я понимаю, но есть ли название для этой странности?
mlissner

2
@mlissner <()также известен как замена процесса .
Мику

1
commПервоначально был написан около 1973 года кем-то из Bell Labs, а не RMS. Вы имеете в виду реализацию GNU, которая появилась намного позже. За эти годы было много разных реализаций утилит Unix.
Стефан

32

из stackoverflow ...

comm -23 file1 file2

-23 подавляет строки, которые есть в обоих файлах или только в файле 2. Файлы должны быть отсортированы (они есть в вашем примере), но если нет, сначала передайте их через sort ...

Смотрите страницу руководства здесь


По какой-то причине это не работает для меня ...
января

@ Джан, твои файлы отсортированы? Как вы их отсортировали?
JJS

8

Методы grep и comm (с сортировкой) занимают много времени на больших файлах. SiegeX и ghostdog74 поделились двумя замечательными методами awk для извлечения строк, уникальных для одного из двух файлов, в переполнении стека:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2

2
Если вы делаете это с огромными файлами, то ограничения памяти при загрузке огромного файла в ассоциативный массив будут непомерными.
Чарльз Даффи

4

Если файлы большие и у вас нет индивидуального порядка для ваших записей, grep занимает слишком много времени. Быстрая альтернатива будет

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[file2-file1 результаты на экран, канал в файл и т. д.]

Переход >к <получит противоположное вычитание.rm 1 2


2

Вы также можете рассмотреть vimdiff, он подчеркивает различия между файлами в редакторе vim


1
Но есть ли простой способ автоматически выполнять вычитание в Vimdiff?
Казарк,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.