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