Команда Unix для поиска строк, общих в двух файлах

144

Я уверен, что однажды нашел команду unix, которая могла печатать общие строки из двух или более файлов, знает ли кто-нибудь ее имя? Это было намного проще, чем diff.

  • 5
    Ответы на этот вопрос не обязательно то, что все захотят, так как comm требует отсортированных входных файлов. Если вы хотите просто построчно, это здорово. Но если вы хотите то, что я бы назвал «антидифф», то comm не делает эту работу.
  • 0
    @ RobertP.Goldman есть способ получить общее между двумя файлами, когда file1 содержит частичный шаблон, такой как pr-123-xy-45 а file2 содержит ec11_orop_pr-123-xy-45.gz . Мне нужен ec11_orop_pr-123-xy-45.gz содержащий ec11_orop_pr-123-xy-45.gz
Показать ещё 1 комментарий
Теги:
command-line

11 ответов

163
Лучший ответ

Команда, которую вы ищете, comm. например: -

comm -12 1.sorted.txt 2.sorted.txt

Здесь:

-1: подавить столбец 1 (строки, уникальные для 1.sorted.txt)

-2: подавить столбец 2 (строки, уникальные для 2.sorted.txt)

  • 26
    Типичное использование: comm -12 1.sorted.txt 2.sorted.txt
  • 36
    Хотя для comm нужны отсортированные файлы, вы можете использовать grep -f file1 file2, чтобы получить общие строки обоих файлов.
Показать ещё 9 комментариев
48

Чтобы легко применить команду comm к несортированным файлам, используйте Bash замену процессов:

$ bash --version
GNU bash, version 3.2.51(1)-release
Copyright (C) 2007 Free Software Foundation, Inc.
$ cat > abc
123
567
132
$ cat > def
132
777
321

Итак, файлы abc и def имеют одну общую линию, одну с "132". Использование comm в несортированных файлах:

$ comm abc def
123
    132
567
132
    777
    321
$ comm -12 abc def # No output! The common line is not found
$

В последней строке не было выхода, общая строка не была обнаружена.

Теперь используйте отсортированные файлы comm, сортируя файлы с заменой процесса:

$ comm <( sort abc ) <( sort def )
123
            132
    321
567
    777
$ comm -12 <( sort abc ) <( sort def )
132

Теперь мы получили строку 132!

  • 2
    итак ... sort abc > abc.sorted , sort dev > def.sorted и затем comm -12 abc.sorted def.sorted ?
  • 1
    @NikanaReklawyks А затем не забудьте удалить временные файлы впоследствии, и справиться с очисткой в случае ошибки. Во многих случаях замена процесса также будет происходить намного быстрее, поскольку вы можете избежать дискового ввода-вывода, пока результаты помещаются в память.
24

Может быть, вы имеете в виду comm?

Сравнение отсортированных файлов FILE1 и FILE2 по строкам.

Без параметров выведите трех столбцов. Колонка 1 содержит строки, уникальные для FILE1, столбец два содержат строки, уникальные для FILE2 и три столбца содержат строки, общие для обоих файлов.

Секрет в поиске этой информации - это информационные страницы. Для программ GNU они гораздо более подробные, чем их man-страницы. Попробуйте info coreutils, и он перечислит вам все полезные полезные утилиты.

19

Чтобы дополнить однострочный Perl, здесь его эквивалент awk:

awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2

Это будет читать все строки из file1 в массив arr[], а затем проверить каждую строку в file2, если она уже существует в массиве (т.е. file1). Найденные строки будут напечатаны в том порядке, в котором они отображаются в file2. Обратите внимание, что сравнение in arr использует всю строку из file2 в качестве индекса для массива, поэтому оно будет сообщать только точные совпадения на всех строках.

  • 2
    Это правильный ответ. Ни один из остальных не может быть настроен на работу вообще (я не пробовал perl , потому что). Спасибо за миллион, мисс
  • 1
    Сохранение порядка при отображении общих строк может быть действительно полезным в некоторых случаях, из-за которых исключается связь.
Показать ещё 1 комментарий
17

Пока

grep -v -f 1.txt 2.txt > 3.txt

дает вам разницу в двух файлах (что находится в 2.txt, а не в 1.txt), вы можете легко выполнить

grep -f 1.txt 2.txt > 3.txt

собрать все общие строки, которые должны обеспечить легкое решение вашей проблемы. Если вы отсортировали файлы, вы должны взять comm, тем не менее. Привет!

  • 2
    grep делает некоторые странные вещи, которые вы можете не ожидать. В частности, все в 1.txt будет интерпретироваться как регулярное выражение, а не простая строка. Также любая пустая строка в 1.txt будет соответствовать всем строкам в 2.txt . Так что это будет работать только в очень специфических ситуациях.
  • 13
    @ChristopherSchultz: Возможно обновить этот ответ, чтобы он работал лучше, используя нотации grep POSIX, которые поддерживаются grep встречающимся в большинстве современных вариантов Unix. Добавьте -F (или используйте fgrep ) для подавления регулярных выражений. Добавьте -x (для точного), чтобы соответствовать только целым строкам.
Показать ещё 2 комментария
8
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2
  • 0
    это работает лучше, чем команда comm как она ищет каждую строку file1 в file2 где comm будет сравниваться, только если строка n в file1 равна строке n в file2 .
  • 1
    @teriiehina: нет; comm не просто сравнивает строку N в файле1 со строкой N в файле2. Он может прекрасно управлять серией строк, вставленных в любой файл (что, конечно, эквивалентно удалению серии строк из другого файла). Это просто требует, чтобы входы были в отсортированном порядке.
Показать ещё 2 комментария
4
awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2
3

Если эти два файла еще не отсортированы, вы можете использовать:

comm -12 <(sort a.txt) <(sort b.txt)

и он будет работать, избегая сообщения об ошибке comm: file 2 is not in sorted order при выполнении comm -12 a.txt b.txt.

  • 0
    Вы правы, но это по сути повторяет другой ответ , который действительно не дает никакой выгоды. Если вы решите ответить на более старый вопрос, на котором уже есть точные и правильные ответы, добавление нового ответа в конце дня может не принести вам никакой пользы. Если у вас есть какая-то отличительная новая информация, или вы убеждены, что все остальные ответы неверны, обязательно добавьте новый ответ, но «еще один ответ», дающий ту же основную информацию спустя долгое время после того, как вопрос задан, обычно выигрывает » Я не заработаю тебе много кредитов.
  • 0
    Я даже не видел этот ответ @JonathanLeffler, потому что эта часть была в самом конце ответа, смешанного с другими элементами ответа раньше. Хотя другой ответ является более точным, мое преимущество, я думаю, состоит в том, что для того, кто хочет быстрого решения, нужно будет прочитать только две строки. Иногда мы ищем подробный ответ, а иногда мы спешим, и быстрый для чтения готовый к вставке ответ - это хорошо.
Показать ещё 2 комментария
3

В ограниченной версии Linux (как QNAP (nas), над которым я работал):
- comm не существует
- grep -f file1 file2 может вызвать некоторые проблемы, как сказал @ChristopherSchultz, и использование grep -F -f file1 file2 было очень медленным (более 5 минут - не закончилось - более 2-3 секунд с использованием метода ниже для файлов более 20 МБ)

Итак, вот что я сделал:

sort file1 > file1.sorted
sort file2 > file2.sorted

diff file1.sorted file2.sorted | grep "<" | sed 's/^< *//' > files.diff
diff file1.sorted files.diff | grep "<" | sed 's/^< *//' > files.same.sorted

Если "files.same.sorted" должен быть в том же порядке, что и исходный, чем добавить эту строку для того же порядка, что и файл1:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same

или, для того же порядка, чем файл2:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same

2

Просто для справки, если кто-то все еще смотрит, как это сделать для нескольких файлов, см. связанный ответ Поиск совпадающих строк по многим файлам.


Объединив эти два ответа (ans1 и ans2), я думаю, вы можете получить результат, который вы не требуя сортировки файлов:

#!/bin/bash
ans="matching_lines"

for file1 in *
do 
    for file2 in *
        do 
            if  [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then
                echo "Comparing: $file1 $file2 ..." >> $ans
                perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans
            fi
         done 
done

Просто сохраните его, дайте ему права выполнения (chmod +x compareFiles.sh) и запустите его. Он примет все файлы, присутствующие в текущем рабочем каталоге, и сделает сравнение all-vs-all, оставив в файле match_lines результат.

Что нужно улучшить:

  • Пропустить каталоги
  • Избегайте сравнения всех файлов два раза (file1 vs file2 и file2 vs file1).
  • Возможно, добавьте номер строки рядом с соответствующей строкой
-1
rm file3.txt

cat file1.out | while read line1
do
        cat file2.out | while read line2
        do
                if [[ $line1 == $line2 ]]; then
                        echo $line1 >>file3.out
                fi
        done
done

Это должно сделать это.

  • 1
    попытался отформатировать ваш код - пожалуйста, проверьте, отредактируйте и улучшите :-)
  • 0
    Спасибо за форматирование.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню