[Resolvido] Dividir um arquivo em linhas que contém um texto específico
Olá pessoal, boa noite!
Eu tenho um arquivo de texto (texto.out) que é composto por vários "blocos". Eu preciso dividir esse arquivo de forma que cada bloco vá para um novo arquivo menor. Porém, cada um desses blocos tem diferentes números de linhas (alguns tem 100, outro 300, não há uma regra quanto a isso). Porém, todos os blocos terminam com o mesmo texto que não se repete em outras partes do arquivo: "END OF GFF DUMP". Então, eu pensei em usar esse "sinal" para delimitar os blocos e divir o arquivo. Pensei em usar while com grep pra fazer isso. Consegui uma lista com o número da linha de todas as vezes que esse sinal aparece no arquivo, mas daí até usar essa informação para dividí-lo está bem difícil pra mim. Segue o código abaixo:
#variables
lines=$(grep -n "END OF GFF DUMP" texto.out | wc -l)
#generate the list with the number of the lines with "END OF GFF DUMP"
i=1
while [ $i -le $lines ];
do
declare -i | grep -n 'END OF GFF DUMP' texto.out | awk -F ':' '{print $1}' | head -n $i | tail -n 1
((i=$i+1))
done
Rodando esse script ele me retorna:
169
437
720
988
1261
1569
1862
2155
2408
...que são os números das linhas que eu tenho interesse no arquivo texto.out. Eu estava querendo fazer algo mais ou menos assim: "$ head -n x texto.out | tail -n 1 > texto$i.out" onde o x seria cada número dessa lista, se possível, colocar isso dentro do while, mas não está dando certo. Alguém tem alguma dica?
Muito obrigado!!
Eu tenho um arquivo de texto (texto.out) que é composto por vários "blocos". Eu preciso dividir esse arquivo de forma que cada bloco vá para um novo arquivo menor. Porém, cada um desses blocos tem diferentes números de linhas (alguns tem 100, outro 300, não há uma regra quanto a isso). Porém, todos os blocos terminam com o mesmo texto que não se repete em outras partes do arquivo: "END OF GFF DUMP". Então, eu pensei em usar esse "sinal" para delimitar os blocos e divir o arquivo. Pensei em usar while com grep pra fazer isso. Consegui uma lista com o número da linha de todas as vezes que esse sinal aparece no arquivo, mas daí até usar essa informação para dividí-lo está bem difícil pra mim. Segue o código abaixo:
#variables
lines=$(grep -n "END OF GFF DUMP" texto.out | wc -l)
#generate the list with the number of the lines with "END OF GFF DUMP"
i=1
while [ $i -le $lines ];
do
declare -i | grep -n 'END OF GFF DUMP' texto.out | awk -F ':' '{print $1}' | head -n $i | tail -n 1
((i=$i+1))
done
Rodando esse script ele me retorna:
169
437
720
988
1261
1569
1862
2155
2408
...que são os números das linhas que eu tenho interesse no arquivo texto.out. Eu estava querendo fazer algo mais ou menos assim: "$ head -n x texto.out | tail -n 1 > texto$i.out" onde o x seria cada número dessa lista, se possível, colocar isso dentro do while, mas não está dando certo. Alguém tem alguma dica?
Muito obrigado!!
Entre ou Registre-se para fazer um comentário.
Comentários
vc quer tipo pegar as primeiras 100 linhas e jogar em outro arquivo ou pegar a primeira coluna das primeiras 100 linhas e jogar em outro arquivo?
sempre quando vou tratar algum dado que não tem uma quantidade certa de linhas, arquivos etc eu dou um ls, cat etc para ler o conteudo e coloco o | wc -l para contar a quantidade de saidas, assim posso determinar quantos loops serão necessarios.
LINHAS=$"0" # Determina o ponto de partida da contagem
TOTAL_LINHAS=$(cat $ENDERECO_DO_ARQUIVO | wc -l) # identifica quantas linhas tem o arquivo, e consequentemente quantos loops terá o while
while [ $LINHAS -ne $TOTAL_LINHAS ];do # a cada loop processa uma linha
LINHAS=$[ $LINHAS + 1 ] # vai somando para não dar loop infinito
# o codigo que faz a filtragem do texto
done # fecha o while
no codigo acima eu conseguiria ler apenas um arquivo, para ele ler todos de uma pasta vc deve fazer outro loop deixando este por dentro, e neste outro loop vc usaria a mesma logica para definir a quantidade de rodadas.
se for o caso de ler linha por linha a cada loop deve se ler uma linha diferente então eu uso a variavel LINHAS que vai somando a cada loop, uso esta variavel no sed:
LINHA_ ATUAL=$(cat $ENDERECO | sed -n ${LINHAS}p) # linha atual
existe formas bem mais simples de se escrever estes loops, mas eu sei fazer assim.
O arquivo onde quero pesquisar é esse (ele é um output de outro programa, é um arquivo de texto bem complicadinho):
C4 Alignment:
------------
Query: xx-like-aa
Target: scaffoldxx|size867476
Model: protein2genome:bestfit
Raw score: 166
Query range: 0 -> 407
Target range: 202895 -> 302143
1 : GluLysMetProAsnSerAsnArgLeuTyrThrLeuAspGluPheLeuIleLysLeuG : 20
! ! |||:::! !..!|||!!!|||! !! !||| !||| !||| |||.
LeuValAsnProSerPheArgArgPheTyrArgSerAspProPheGluIleCysLeuA
202896 : TTAGTTAATCCGTCATTCCGTCGTTTTTATAGATCCGATCCATTTGAGATTTGTTTGA : 202953
.
.
.
scaffold210|size867476 exonerate:protein2genome:bestfit splice3 302097 302098 . + . intron_id 3 ; splice_site "AG"
scaffold210|size867476 exonerate:protein2genome:bestfit cds 302099 302143 . + .
scaffold210|size867476 exonerate:protein2genome:bestfit exon 302099 302143 . + . insertions 0 ; deletions 0
scaffold210|size867476 exonerate:protein2genome:bestfit similarity 202896 302143 166 + . alignment_id 1 ; Query Cmeg-Or88a-like-aa ; Align 202896 1 78 ; Align 300821 28 153 ; $
# --- END OF GFF DUMP ---
#
C4 Alignment:
------------
Query: xx-like-aa
Target: scaffoldxx|size867476
Model: protein2genome:bestfit
Raw score: 83
Query range: 0 -> 407
Target range: 72802 -> 822917
.
.
.
#!/bin/bash
#Defino lines com o número total de blocos que há no arquivo
lines=$(grep -n "END OF GFF DUMP" texto.out | wc -l)
#Dividir o arquivo texto.out em seus diferentes blocos baseado na linha "END OF GFF DUMP"
i=1
while [ $i -le $lines ];
do
#Defini "a" que seria o número da linha do segundo "END OF GFF DUMP" encontrado e "b" que seria o primeiro.
a=$(grep -n "END OF GFF DUMP" texto.out | cut -d: -f1 | head -n $i | tail -n 1)
b=$(grep -n "END OF GFF DUMP" texto.out | cut -d: -f1 | head -n $(( i - 1 )) | tail -n 1)
echo "Começo do arquivo número $i -----------------------------------------------" > texto$i.out
#tudo o que está entre `grep...` é apenas para dar o número da linha para o head -n. Assim o head vai pegar desde o início do arquivo até o final daquele bloco. No tail eu subtraio o "b" de "a" para conseguir pegar o bloco corretamente, excluindo o começo do arquivo, e salvo no arquivo texto$i.out
head -n `grep -n "END OF GFF DUMP" texto.out | cut -d: -f1 | head -n $i | tail -n 1` texto.out | tail -n $(( a - b )) >> texto$i.out 2>&1
echo "Final do arquivo número $i -----------------------------------------------" >> texto$i.out
((i=$i+1))
done
Muito obrigado pela ajuda e pelas dicas!! Forte abraço!
eu faria algo semelhante só que escrevendo linha por linha e quando chegasse no "END OF GFF DUMP" eu mudaria de arquivo e continuaria esta escrita, o meu ficaria bem mais lento que o seu, parabéns!
Qualquer coisa dá uma olhada no shell script do zero, mas acho que vc já não é mais iniciante
http://www.mediafire.com/file/4x4yadaf8s9c7tx/Shell+Script+do+Zero.pdf
Abraços!