
У меня есть текстовый файл со строками/именами файлов в отдельных строках, например. 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