unix: obter caracteres de 10 a 80 em um arquivo

4

Eu tenho um arquivo contendo texto separado por linha:

GCAACACGGTGGGAGCACGTCAACAAGGAGTAATTCTTCAAGACCGTTCCAAAAACAGCATGCAAGAGCG
GTCGAGCCTAGTCCATCAGCAAATGCCGTTTCCAGCAATGCAAAGAGAACGGGAAGGTATCAGTTCACCG
GTGACTGCCATTACTGTGGACAAAAAGGGCACATGAAGAGAGACTGTGACAAGCTAAAGGCAGATGTAGC

A partir disso, quero extrair caracteres de 10 a 80, então:

TGGGAGCACGTCAACAAGGAGTAATTCTTCAAGACCGTTCCAAAAACAGCATGCAAGAGCG
GTCGAGCCT

Eu encontrei como contar os caracteres em um arquivo:

  wc -m file

e como obter um número de caracteres por linha:

 awk '{print substr($0,2,6)}' file

mas não consigo encontrar uma maneira de obter os caracteres de 10 a 80.

Novas linhas não contam como caracteres.

Alguma idéia?

Sim, isso é DNA, de um genoma completo. Eu extraí esse bit de DNA de um arquivo fasta contendo diferentes scaffolds (10 e 11 neste caso) usando

 awk '/scaffold_10\>/{p=1;next} /scaffold_11/{p=0;exit} p'
Em última análise, eu gostaria de ter um comando simples para obter caracteres de 100 a 800 (ou algo parecido) daquele scaffold especificado.

EDIT: A pergunta continua aqui: use gff2fasta em vez de um script bash para obter partes de seqüências de DNA de um genoma completo

    
por gugy 06.04.2017 / 10:33

6 respostas

6

Eu me pergunto como o feed de linha no arquivo deve ser tratado. Isso conta como um personagem ou não?

Se precisarmos apenas do byte 10 e imprimir 71 bytes (A, C, T, G e linefeed), a solução Sato Katsura será a mais rápida (assumindo aqui o GNU dd ou compatível para status=none , substitua por 2> /dev/null (embora isso também esconderia mensagens de erro, se houver) com outras implementações):

 dd if=file bs=1 count=71 skip=9 status=none

Se o feed de linha deve ser ignorado, filtre-os com tr -d '\n' :

 tr -d '\n' < file | dd bs=1 count=70 skip=9 status=none

Se o cabeçalho do Fasta deve ser ignorado, é:

 grep -v '^[;>]' file | tr -d '\n' | dd bs=1 count=70 skip=9 status=none

grep -v '^[;>]' file significa ignorar todas as linhas que começam com ; ou > .

    
por 06.04.2017 / 14:43
6
$ cat file1
GCAACACGGTGGGAGCACGTCAACAAGGAGTAATTCTTCAAGACCGTTCCAAAAACAGCATGCAAGAGCG
GTCGAGCCTAGTCCATCAGCAAATGCCGTTTCCAGCAATGCAAAGAGAACGGGAAGGTATCAGTTCACCG
GTGACTGCCATTACTGTGGACAAAAAGGGCACATGAAGAGAGACTGTGACAAGCTAAAGGCAGATGTAGC

verifique o comprimento de cada linha

$ awk '{print length,$0}' file1
70 GCAACACGGTGGGAGCACGTCAACAAGGAGTAATTCTTCAAGACCGTTCCAAAAACAGCATGCAAGAGCG
70 GTCGAGCCTAGTCCATCAGCAAATGCCGTTTCCAGCAATGCAAAGAGAACGGGAAGGTATCAGTTCACCG
70 GTGACTGCCATTACTGTGGACAAAAAGGGCACATGAAGAGAGACTGTGACAAGCTAAAGGCAGATGTAGC

imprima os caracteres de 10 a 80

$ awk '{print substr($0,10,70)}' RS= file1
TGGGAGCACGTCAACAAGGAGTAATTCTTCAAGACCGTTCCAAAAACAGCATGCAAGAGCG
GTCGAGCC

Isso pressupõe que a entrada não contenha linha vazia ( RS= ativa o modo de parágrafo onde cada registro é um parágrafo (parágrafos sendo delimitados por sequências de linhas vazias)) e implica carregar o arquivo inteiro na memória.

    
por 06.04.2017 / 11:10
5

Para bytes (assim também funcionaria para caracteres de byte único, como em sua amostra):

dd bs=1 skip=9 count=71 < file 2> /dev/null

Ou mais eficientemente com o GNU dd :

dd iflag=fullblock,skip_bytes,count_bytes skip=9 count=71 status=none < file

Para caracteres com zsh :

{
  IFS= read -ru0 -k9 discard &&
    IFS= read -ru0 -k71 text &&
    printf %s $text
} < file

(não imprime nada se houver menos de 80 caracteres no arquivo).

ksh93 e bash têm uma opção -N semelhante a zsh -k , mas eles não suportam os caracteres NUL e o bash tem bugs .

Com o GNU awk :

awk -v RS='.{1}' -v ORS= 'NR>=10 {print RT}; NR == 80 {exit}'

Usamos .{1} como . , sendo que um único caractere não seria tratado como um regexp.

Outra opção é converter para uma codificação de caracteres que tenha um número fixo de bytes por caractere (e possua todos os caracteres possíveis) como UTF-32LE, que possui 4 bytes por caractere:

< file iconv -t UTF-32LE |
   dd bs=4 skip=9 count=71 2> /dev/null |
   iconv -f UTF-32LE
    
por 06.04.2017 / 15:59
3

Se você não se importa em trazer todo o conteúdo para a memória, e ter a linha desembrulhada , você pode usar a substituição de comandos para ler (graças a George Vasiliou para a melhoria tr !)

data=$( tr -d '\n' < inputfile )

imprima a partir de (baseado em zero) 10, para um comprimento de 70 bytes:

printf "%s\n" "${data:9:70}"
    
por 06.04.2017 / 14:11
2
perl -l -0777pe '
   my($start, $stop) = qw/10 80/; $delta = $stop - $start--;
   (undef, $_, $a) = unpack "A${start}A${delta}A*";
   $_ .= $1 while length() - y/\n/\n/ < $delta and $a =~ /(.)/g;
'  scaffolded_file_10
    
por 06.04.2017 / 16:55
2

Assumindo que os caracteres de nova linha não são significativos para os dados, mas apenas formatando no arquivo (código não testado):

BEGIN {
  linesize=70;
  start=10;
  end=80;
}
// {
  if ((NR>=int(start/linesize) && (NR<=int(end/linesize)) {
     from = NR==int(start/linesize) ? start % linesize : 0;
     to   = NR==int(end/linesize) ? (end % linesize)-from : linesize+1;
     print substr($0, from, to);
  }
  if (NR==int(end/linesize)) exit;
}
    
por 06.04.2017 / 15:20