Как прочитать входной файл строк, сопоставить и изменить соответствия на месте?

Как прочитать входной файл строк, сопоставить и изменить соответствия на месте?

У меня есть текстовый файл со строками/именами файлов в отдельных строках, например. filename.txt. Там сотни имен файлов

ABC123_S386_R1_001
JKL345_S441_R1_001
filename9000_S587_R1_001

и еще один текстовый файл со строками/именами файлов и дополнительными данными, например results.txt:

>ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

Теперь не все имена файлов в filename.txtприсутствуют в results.txt, и они не в порядке. Я хочу вставить префикс ко всем именам файлов от filename.txtдо results.txt, но не к остальным.

Как прочитать входной файл строк, сопоставить его с другим файлом и изменить соответствия?

Раньше я сопоставлял отдельные имена файлов с помощью sequence.txt, получал их номера строк и использовал sedих для изменения либо отдельной строки, либо блока строк.

Мой желаемый результат будет выглядеть так:

>h-19/US/CA-ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

где h-19/US/CA-находится суффикс, который я хотел бы добавить ко всем совпадениям.

Изменить: >— это первый символ всех строк, которые необходимо изменить, перед ним нет никаких символов, >а после имени файла — никаких пробелов.

решение1

Предполагая, что соответствующие строки results.txtне содержат пробелов после имени файла, следующая awkпрограмма будет работать:

awk -v prefix="h-19/US/CA-" 'NR==FNR{fnames[$1]; next} \
    /^>/{name=substr($0,2); if (name in fnames) {sub(/^>/, ">" prefix)} }1' filenames.txt results.txt
  • Сначала будет выполнен анализ filenames.txt, а затем results.txt.
  • При синтаксическом анализе filenames.txt(где FNRсчетчик строк для каждого файла равен NRглобальному счетчику строк) он зарегистрирует все имена файлов (которые являются единственными полями в строке) в массиве fnames, но затем немедленно перейдет к выполнению следующей строки.
  • При разборе results.txtон проверит, начинается ли строка с >. Если это так, он проверит, nameнаходится ли подстрока, следующая за этим символом (временно сохраненная в ), среди «индексов массива» fnames. Если это так, он будет использовать sub()для замены начального символа >+ >префикс, переданный awkкак переменная prefix(через -vдирективу).
  • Явно «заблудившаяся» строка 1даст команду awkнапечатать текущую строку, включая все возможные модификации (но только на , results.txtтак как при обработке первого файла мы не достигнем этой части).

Обратите внимание, что awkсамо по себе не может изменять файлы на месте, поэтому вам придется работать с временным файлом. Если у вас достаточно новая версия GNU Awk (> 4.1.0), вы можете использовать расширение inplace; конечно, тогда вам нужно будет отключить опцию для filenames.txtфайла:

awk -i inplace -v prefix=" ... " ' ... ' inplace=0 filenames.txt inplace=1 results.txt

Это отключит редактирование на месте для filenames.txtи снова включит для results.txt.

решение2

С помощью этого инструмента sedвы можете собрать имена файлов в области удержания, а затем results.txtпроверить все строки на совпадения, чтобы отфильтровать строки, которые следует изменить:

sed -e '1,/^$/{H;1h;d;}' -e 'G;/^>\(.*\).*\n\1\n/s_^>_>h-19/US/CA-_;P;d' filename.txt <((echo)) results.txt
  • Вы видите, что я передаю пустую строку <((echo))между файлами, поэтому 1,/^$/обращаюсь ко всем строкам первого файла (и к пустой строке)
  • Эти строки добавляются для удержания пространства, а затем удаляются с помощью H;1h;d( 1hчтобы избежать начала удержания пространства с новой строки)
  • Gдобавляет удерживаемый пробел ко всем строкам result.txtи /^>\(.*\).*\n\1\n/сопоставляет те строки, которые начинаются с >и строки, которая является именем файла (заключенным в новые строки в удерживаемом пробеле)
  • s_^>_>h-19/US/CA-_делает ли замена для этих строк
  • P;dпечатает только первую строку без добавленного мусора. Вы могли бы сделать s/\n.*//вместо этого

решение3

Используйте perlдля внесения изменений во входной файл:

pfx='h-19/US/CA-' \
perl -pi -e '
  BEGIN { %h = map { tr/\n//dr => $ENV{pfx}} <STDIN>}
  s/^>\K(?=(.*))/$h{$1}/;
' results.txt < filename.txt

Связанный контент