← Весь справочник

comm

Сравнить два отсортированных списка — что есть только в первом, только во втором, в обоих

comm берёт два отсортированных файла и раскладывает строки по трём колонкам: только в первом, только во втором, в обоих сразу. Это быстрый способ найти пересечение и разность двух списков: пользователей, пакетов, IP-адресов, имён файлов.

Синтаксис

comm [опции] файл1 файл2
comm -12 <(sort a) <(sort b)       # если файлы ещё не отсортированы

Один из файлов (но не оба) может быть - — стандартный ввод.

Формат вывода

Без опций — три колонки, разделённые табуляцией:

КолонкаОтступЧто в ней
1без отступастроки, которые есть только в файл1
2одна табуляциястроки, которые есть только в файл2
3две табуляциистроки, которые есть в обоих

Опции

Какие колонки показать

Опции скрывают колонки; их комбинируют: -12 = -1 -2.

ОпцияЧто остаётсяСмысл
-12колонка 3пересечение: есть в обоих
-23колонка 1разность: есть в первом, нет во втором
-13колонка 2разность: есть во втором, нет в первом
-3колонки 1 и 2симметрическая разность: всё, что не совпало
-1 / -2—скрыть одну колонку

Прочее

ОпцияЧто делает
--check-orderпроверять сортировку и остановиться с ошибкой на первой же неотсортированной строке
--nocheck-orderне проверять сортировку вообще
--output-delimiter=STRразделять колонки строкой STR вместо табуляции
--totalв конце добавить строку с количеством строк в каждой колонке и словом total
-zстроки разделены нулевым байтом, а не \n

По умолчанию (без --check-order и --nocheck-order) comm замечает беспорядок только там, где строки не нашли пару: печатает file 1 is not in sorted order, но продолжает работу и в конце возвращает код 1.

Примеры

printf 'apple\nbanana\ncherry\n' > a.txt
printf 'banana\ncherry\ndate\n' > b.txt

# все три колонки
comm a.txt b.txt

# пересечение
comm -12 a.txt b.txt

# только в a.txt / только в b.txt
comm -23 a.txt b.txt
comm -13 a.txt b.txt

# неотсортированный ввод — сортируем на лету
printf 'cherry\napple\n' > c.txt
comm -12 <(sort c.txt) <(sort a.txt)

# один файл из стандартного ввода
printf 'date\nbanana\n' | sort | comm -12 - b.txt

# колонки через запятую и сводка
comm --output-delimiter=, --total a.txt b.txt

# сколько строк общих
comm -12 a.txt b.txt | wc -l

# строгая проверка сортировки в скрипте
comm --check-order -12 a.txt b.txt || echo 'input is not sorted'

Подводные камни

  • Ввод обязан быть отсортирован так же, как его сортирует sort в текущей локали. На неотсортированном вводе comm выдаёт неверный результат: строки, которые есть в обоих файлах, попадают в «только в первом» и «только во втором».
  • Сортируй в той же локали, что и запускаешь comm. Если файл отсортирован с LC_ALL=C, а comm работает в en_US.UTF-8 (или наоборот), порядок может разойтись. Надёжно — задать одну локаль на весь конвейер: LC_ALL=C sort … и LC_ALL=C comm ….
  • sort -n не годится. comm сравнивает строки как текст, поэтому числовая сортировка (9 раньше 10) для него — беспорядок. Используй обычный sort.
  • --nocheck-order только прячет предупреждение, неверный результат никуда не девается, а код возврата становится 0.
  • Повторы считаются поштучно. Если в первом файле a дважды, а во втором один раз, одна a попадёт в общие, а вторая — в «только в первом». Нужны множества — сначала sort -u.
  • Пробелы в конце строки и \r делают строки разными. apple и apple\r (файл из Windows) не совпадут. Проверь через cat -A.
  • Нет опции «без учёта регистра» (-i нет). Приведи регистр заранее: tr 'A-Z' 'a-z', затем sort.
  • Колонки разделены табуляцией, поэтому в выводе без -12/-23/-13 строки «съезжают». Для обработки в скрипте оставляй одну колонку или задай --output-delimiter.

См. также

sort · uniq · join · diff, cmp и patch · grep