Извлечение нескольких экземпляров текста между двумя словами, включая стартовое слово, но исключающее слово

Файл PDB содержит многочисленные абзацы конформаций белка.

Каждая конформация начинается с ключевого слова ATOM и заканчивается ключевым словом END .

Я пытаюсь прочитать файл в bash, чтобы прочитать каждую строку из ATOM до END, но я не хочу читать слово END.

Я хочу сделать это для каждой конформации (абзацы) и хранить каждый абзац в массиве.

Файл выглядит примерно так:

ATOM line 1... ATOM line 2... ATOM line 3... # More lines.... END ATOM line 1... ATOM line 2... ATOM line 3... # more lines... END 

ATOM- END является одной конформацией.

Я хочу иметь возможность читать каждую конформацию в массив, включая ATOM, но исключая END .

Я могу читать текст между двумя ключевыми словами, исключая оба слова, но я не знаю, как включить начальное слово, но исключить конечное слово.

Также считывая каждую конформацию в массив, такой, что conf[0] = первая конформация, conf[1] = вторая конформация так далее и т. Д. Не работает.

Код:

 #!/bin/bash filename='coor.pdb' echo Start i=0 while read line; do conf[$i]=$(sed -n '/ATOM/,/END/{//!p}') i=i+1 done < $filename echo $conf[0] > first_frame.data 

2 Solutions collect form web for “Извлечение нескольких экземпляров текста между двумя словами, включая стартовое слово, но исключающее слово”

 #!/bin/bash filename='coor.pdb' echo Start i=1 input=false while read -r line do if [ "${line%% *}" == "ATOM" ] then input=true elif [ "${line%% *}" == "END" ] then ((i++)) # increase variable i by 1 == (i+1) rm -f "${i}_frame.data" # remove ${i}_frame.data" if exist input=false # stop output lines until next ATOM fi if $input # if var INPUT is true add line to ${i}_frame.data file then echo "$line" >> "${i}_frame.data" fi done < "$filename" 

В будущем некоторые подсказки:

 sed '/ATOM/,/END/!d;/END/d' sed -n '/ATOM/{:;N;s/\nEND//;T;p}' 

Таким образом, вы можете сделать задачу:

 nl -s'.frame.data' -bp"^END" coor.pdb | sed -n '/ATOM/{s/^/echo \"/;:;s/ \{6,\}//;N;s/END//;T;s/\n */\">/p}' | bash 

Обработка текста в bash медленная. Ручная манипуляция Pure-bash хороша для текста, который у вас уже есть в переменных, или для чтения очень маленьких файлов. Я подозреваю, что файлы вычислительной биологии обычно не будут маленькими, поэтому используйте инструмент типа awk который имеет небольшую начальную стоимость, но обрабатывает текст намного быстрее, чем bash.

Предполагая, что вы действительно хотите разбить файл pdb :

 awk -v RS='\nEND\n' '{ fn="frame" NR ".pdb"; print > fn; close(fn) }' "$filename" 

Получите awk, чтобы использовать \nEND\n в качестве разделителя входных данных вместо новой строки, тогда вы даже можете использовать свой счетчик записей. Сепаратор выходной записи по-прежнему является значением по умолчанию ORS="\n" . (очень приятное предложение от Costas. Я изменил его так, чтобы END должен был быть в начале строки и был добавлен close чтобы убедиться, что он не использует тонну дескрипторов файлов на входах с очень большим количеством конформаций.)

Моя оригинальная идея:

 awk 'BEGIN{i=0; fn="frame0.pdb"} !/^END/ { print > fn; } /^END/{ close(fn); fn="frame" ++i ".pdb"; }' \ "$filename" 

awk кэширует файловые дескрипторы, поэтому несколько print > fn не приводят к закрытию повторного открытия файла. ( close(fn) делает это. Это просто для эффективности, поэтому awk не заканчивается лодками открытых файлов.)

Логика: напечатать каждую полную строку до текущего имени файла. Когда вы увидите строку END , перейдите к следующему имени файла. Если после последнего END нет другой строки, новое имя файла никогда не будет записано, и никакой восстановленный последний файл не будет создан.

OTOH, если вы хотите что-то сделать с массивом линейных блоков в памяти:

 # add a `!/^END/` condition to the concat block if you want to avoid a stray newline after each END awk 'BEGIN{i=0} !/^END/ { arr[i] = arr[i] $0 "\n"; } # concat onto this array element /^END/ { i++; } END{for (k in arr) { print arr[k]; > ("frame" k ".pdb") } }' \ "$filename" 

Затем в блоке END вас есть массив строк awk. Он имеет хорошие регулярные выражения.


Неудачная попытка bash вождения sed (nvm, не работает, потому что sed не читает один байт по времени, как это делает оболочка):

 i=0 while true; do outf="frame${i}.data"; ##### DON'T USE THIS, sed READS TOO MUCH ##### strace -o sed.tr sed '/^END/q42' > "$outf"; # strace to see that the 2nd sed invocation finds the file empty ret=$?; ((i++)); if [[ $ret == 0 ]];then # sed didn't see END before EOF [[ -s $outf ]] || rm -f "$outf"; # clean up empty last file break; elif [[ $ret != 42 ]]; then echo some other sed error; break; fi; done < "$filename" 
  • Отправка настраиваемого контента на основе определенного поиска в файле журнала
  • Удалить до первого вхождения, отредактировать эту строку и распечатать оставшиеся строки без изменений
  • Сопоставьте начало линии с чем-то там?
  • Как сгладить журналы для списка шаблонов и сделать из него отчет?
  • Как найти строки, содержащие более 100 символов, и содержит «if»?
  • Регулярные команды и команды с командой sed
  • Почему не матч SHA?
  • Grep для нескольких строк, покажите количество строк после одной из строк (но не другой)
  • Элегантный способ подсчета, сколько раз шаблоны из файла встречаются в другом файле
  • Самый простой способ найти / заменить в файле с помощью списка?
  • Как sed заменить этот конец линии?
  • Interesting Posts
    Linux и Unix - лучшая ОС в мире.