2025, Nov 14 03:02
Как объединить два TSV‑файла по столбцу в awk без потерь
Показываем, как надёжно объединить два TSV‑файла в awk по столбцу: задать FS/OFS как табы, кэшировать строки и вывести конкатенацию без потерь. Примеры команд.
Слияние двух больших TSV‑файлов по заданному ключу выглядит тривиальным до тех пор, пока не всплывают детали. Типичная ситуация: сопоставить первый столбец одного файла с пятым столбцом другого и вывести одну таб‑разделённую строку, объединяющую обе записи. Сложность в том, чтобы заставить awk и правильно сопоставлять, и сохранять границы табуляций — не напечатав не ту сторону и не «схлопнув» пробелы.
Постановка задачи
У нас есть два файла с табуляцией в качестве разделителя. В первом файле идентификаторы находятся в первом столбце:
anno1.g20653.t1 anno1.g20674.t1 eud1g02416 eud1g02458 27 +
anno2.g3796.t1 anno1.g20698.t1 eud1g02520 eud1g02556 28 +
Во втором — позиционные метаданные, при этом совпадающий идентификатор — в пятом столбце:
scaffold_1 transcript 11256 13613 anno1.g20653.t1
scaffold_1 transcript 25598 47989 anno1.g20066.t2
Нужно вывести только объединённые записи, где столбец 1 из первого файла равен столбцу 5 из второго, формируя одну таб‑разделённую строку на каждое совпадение:
anno1.g20653.t1 anno1.g20674.t1 eud1g02416 eud1g02458 27 + scaffold_1 transcript 11256 13613 anno1.g20653.t1
Что не так в наивной попытке
Следующая попытка строит карту присутствия по первому столбцу первого файла, а затем пытается выбрать совпадающие строки из второго, но на выходе оказываются только строки из второго файла:
awk '
FNR==NR { keep[$1]; next }
FNR>1 && ($5 in keep)
' File1 File2 > File3
Так происходит по двум причинам. Во‑первых, скрипт вовсе не сохраняет содержимое строк первого файла, а только ключи, поэтому при совпадении нечего печатать из file1. Во‑вторых, программа не задаёт FS и OFS как табы, из‑за чего поля разбиваются по произвольным пробелам, а выводные поля склеиваются одним пробелом — рискованно для TSV‑данных.
Суть проблемы
Чтобы получить объединённую запись, awk нужен полный совпавший ряд из первого файла вместе с текущей строкой второго файла. Хранение только ключей из первого файла ограничивает вас простой фильтрацией строк второго без контекста. Кроме того, при обработке TSV явно задавайте FS и OFS равными символу табуляции, чтобы сохранять пустые поля и точные границы табов.
Рабочее решение на awk
Надёжный подход — сохранить целые строки из первого файла в кэше по ключу столбца соединения, а при чтении второго искать совпадения и печатать конкатенацию в таб‑разделённом виде. Эта версия выводит строки в порядке второго файла и исходит из уникальности: первый столбец в первом файле и пятый столбец во втором не повторяются:
awk '
BEGIN { FS=OFS="\t" }
NR==FNR { cache[$1] = $0; next }
($5 in cache) { print cache[$5], $0 }
' file1 file2
Если хотите, чтобы порядок следовал первому файлу, поменяйте порядок входных файлов и инвертируйте поиск. Эта версия сохраняет строки второго файла по его пятому столбцу, затем проходит по первому:
awk '
BEGIN { FS=OFS="\t" }
NR==FNR { cache[$5] = $0; next }
($1 in cache) { print $0, cache[$1] }
' file2 file1
Если в каждом входном файле есть заголовок, который нужно пропустить, вставьте защиту, чтобы пропустить первую запись каждого файла:
awk '
BEGIN { FS=OFS="\t" }
FNR==1 { next }
NR==FNR { cache[$1] = $0; next }
($5 in cache) { print cache[$5], $0 }
' file1 file2
Эти программы явно устанавливают FS и OFS в табуляцию, хранят в памяти целые совпадающие строки из одного файла и печатают по одной объединённой строке TSV на каждое совпадение.
Почему детали важны
На поведение и производительность влияют два допущения. Во‑первых, подход опирается на уникальные ключи по обе стороны соединения: первый столбец в первом файле и пятый — во втором. Если есть дубликаты, для каждого ключа останется только последняя сохранённая строка. Во‑вторых, скрипт держит целиком один файл в памяти, чтобы выполнять поиски за постоянное время; это ускоряет объединение, но объём памяти растёт вместе с размером хранимого файла. Порядок вывода напрямую определяется тем, какой файл читается вторым.
Выводы
При объединении TSV в awk всегда выставляйте FS и OFS в "\t", чтобы избежать ловушек с пробельными символами; храните полные строки на стороне, по которой строите кэш, чтобы можно было их конкатенировать; учитывайте уникальность ключей, объём памяти и порядок вывода. При таких условиях объединение файлов по столбцу получается одновременно лаконичным и надёжным.