O processamento de texto no bash é lento. A manipulação de string pura é boa para o texto que você já possui em variáveis, ou para ler arquivos muito pequenos. Eu suspeito que os arquivos de biologia computacional normalmente não serão pequenos, então use uma ferramenta como awk
, que tem um custo de inicialização pequeno, mas processa o texto muito mais rapidamente que o bash.
Supondo que você realmente queira apenas dividir seu arquivo pdb
:
awk -v RS='\nEND\n' '{ fn="frame" NR ".pdb"; print > fn; close(fn) }' "$filename"
Obtenha o awk para usar \nEND\n
como o separador de registro de entrada, em vez de nova linha, e você pode até mesmo usar seu contador de registros. O separador de registro de saída ainda é o padrão ORS="\n"
. (sugestão muito legal de Costas. Eu ajustei-o assim END
tem que estar no começo de uma linha, e adicionei close
para ter certeza que não usa uma tonelada de descritores de arquivo em entradas com muitas conformações. )
Minha ideia original era:
awk 'BEGIN{i=0; fn="frame0.pdb"}
!/^END/ { print > fn; }
/^END/{ close(fn); fn="frame" ++i ".pdb"; }' \
"$filename"
O awk armazena em cache as manipulações de arquivos, portanto, vários print > fn
não resultam na reabertura do arquivo. ( close(fn)
faz isso. Está lá apenas por eficiência, então o awk não acaba com arquivos de arquivos abertos).
A lógica é: imprimir toda a linha completa para o nome do arquivo atual. Quando você vir uma linha END
, vá para o próximo nome de arquivo. Se não houver outra linha após o último END
, o novo nome do arquivo nunca será gravado e nenhum último arquivo vestigial será criado.
OTOH, se você quiser fazer algo com uma matriz de blocos de linha na memória:
# 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"
Então você tem um array de linhas para fazer o que quiser no bloco END
. Tem boas funções regex.
Falha na tentativa no bash de executar o sed (nvm, falha porque sed
não lê um byte de cada vez da maneira como o shell read
faz):
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"