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", чтобы избежать ловушек с пробельными символами; храните полные строки на стороне, по которой строите кэш, чтобы можно было их конкатенировать; учитывайте уникальность ключей, объём памяти и порядок вывода. При таких условиях объединение файлов по столбцу получается одновременно лаконичным и надёжным.