join
Объединить два файла по общему полю, как JOIN в SQL — внутреннее, внешнее, «без пары»
join соединяет строки двух файлов, у которых совпадает значение ключевого поля, — как JOIN в SQL. Например, список id имя и список id роль превращаются в id имя роль. Оба файла должны быть отсортированы по ключу.
Синтаксис
join [опции] файл1 файл2
join -t, -1 2 -2 1 файл1 файл2 # ключ: 2-е поле первого и 1-е поле второго
Один из файлов (но не оба) может быть - — стандартный ввод.
Формат вывода
По умолчанию для каждой пары строк печатается: ключ, затем остальные поля из первого файла, затем остальные поля из второго. Разделитель на выходе — пробел (или символ из -t). Если ключ повторяется в обоих файлах, выводятся все сочетания: 2 строки × 2 строки = 4 строки.
Опции
Ключ и разделитель
| Опция | Что делает |
|---|---|
-1 N | ключ в первом файле — поле N (по умолчанию 1) |
-2 N | ключ во втором файле — поле N |
-j N | одно и то же поле в обоих файлах (-1 N -2 N) |
-t C | разделитель полей на входе и выходе — один символ (-t,, -t:). Табуляция: -t $'\t' |
-i | сравнивать ключи без учёта регистра |
Без -t поля разделяются пробелами и табуляциями, ведущие пробелы игнорируются, а на выходе ставится одиночный пробел.
Какие строки выводить (типы JOIN)
| Опция | Аналог SQL | Что выводится |
|---|---|---|
| (без опций) | INNER JOIN | только строки, у которых нашлась пара |
-a 1 | LEFT JOIN | плюс строки первого файла без пары |
-a 2 | RIGHT JOIN | плюс строки второго файла без пары |
-a 1 -a 2 | FULL JOIN | плюс непарные из обоих |
-v 1 | анти-join | только строки первого файла без пары |
-v 2 | анти-join | только строки второго файла без пары |
Формат строки
| Опция | Что делает |
|---|---|
-o 1.2,2.3 | вывести только перечисленные поля: файл.поле; 0 — ключ |
-o auto | число полей берётся по первой строке каждого файла — нужно, чтобы -e заполнял пропуски |
-e СТРОКА | чем заполнять отсутствующие поля (работает вместе с -o) |
--header | первая строка каждого файла — заголовок: печатается один раз, в паре не участвует |
Проверка сортировки
| Опция | Что делает |
|---|---|
--check-order | остановиться с ошибкой на первой неотсортированной строке |
--nocheck-order | не проверять (и не предупреждать) |
-z | строки разделены нулевым байтом |
По умолчанию join предупреждает о беспорядке (is not sorted) только если встретил строку без пары, и тогда возвращает код 1.
Примеры
printf '1 alice\n2 bob\n3 carol\n' > users.txt
printf '1 admin\n3 dev\n4 ops\n' > roles.txt
# inner join по первому полю
join users.txt roles.txt
# left join: bob без роли тоже попадёт
join -a 1 users.txt roles.txt
# full join с заполнителем пропусков
join -a 1 -a 2 -e NULL -o auto users.txt roles.txt
# те, у кого нет роли / роли без пользователя
join -v 1 users.txt roles.txt
join -v 2 users.txt roles.txt
# CSV: ключ — 2-е поле в первом файле и 1-е во втором, вывести имя и роль
printf 'alice,10\nbob,20\n' > people.csv
printf '10,admin\n20,dev\n' > ids.csv
join -t, -1 2 -2 1 -o 1.1,2.2 <(sort -t, -k2,2 people.csv) <(sort -t, -k1,1 ids.csv)
# файлы с заголовком
printf 'id name\n1 alice\n2 bob\n' > hu.txt
printf 'id role\n1 admin\n' > hr.txt
join --header -a 1 -e - -o auto hu.txt hr.txt
# сортировка на лету по ключу
join <(sort -k1,1 users.txt) <(sort -k1,1 roles.txt)
Подводные камни
- Оба файла сортируй по ключу и как текст.
joinожидает тот же порядок, что даётsort -k1,1(илиsort -t, -kN,Nдля своего поля) в той же локали. Числовая сортировка не подходит: послеsort -nпорядок2, 10дляjoin— беспорядок, и строки с ключом10могут не найти пару и пропасть. - Неотсортированный ввод не всегда даёт ошибку. Если все строки случайно нашли пару, предупреждения нет. Если нет — часть строк пропадает, а в stderr появляется
input is not in sorted order. В скриптах добавляй--check-order, чтобы падать сразу. - Заголовок без
--headerсчитается данными. Он участвует в сравнении и в сортировке:sortуберёт строкуid nameв конец, после цифр, и порядок дляjoinсломается. Либо--header, либо отрежь заголовок (tail -n +2) до сортировки. -eбез-oне работает.join -a1 -e NULLвыведет короткую строку без заполнителя. Добавь-o autoили явный список полей.-t— только один символ. Многосимвольный разделитель (::,,) не поддерживается — сначала замени его, например через sed.- Без
-tвыравнивание пробелами теряется: на выходе поля склеиваются одиночным пробелом. Для красивой таблицы пропусти результат через column-t. - Регистр и
\rважны.Alice≠alice(есть-i), а ключ1\rиз Windows-файла не совпадёт с1. - Повторы ключей перемножаются. Две строки с ключом
1в первом файле и три во втором дадут шесть строк — проверь данные на дубли черезuniq -d.