sed com quantificadores numéricos - como fazer?

Oct 02 2020

Estou tentando editar cabeçalhos fasta de vários arquivos, a fim de remover uma barra e tudo o que vem depois dela (contanto que 'tudo que vem depois' seja igual ou menor que 10 caracteres). As linhas de cabeçalho são marcadas por '>'.

for i in ./*.fa;do sed -r 's/(>.*)\/.\{,10\}\n/\1\n/' "$i"; done

Eu também tentei

for i in ./*.fa;do sed -r 's/(>.*)\/.{,10}\n/\1\n/' "$i"; done

mas não parece ser melhor. Meu palpite é que é o quantificador {, 10} que quebra as coisas. Eu não tenho certeza embora. Ajuda seria muito apreciada!

Por exemplo, se o seguinte estiver em um arquivo:

>header1_some_extra_data_here/1-1000
ATGCGGGTACCCCA
>code/header2_some_extra_data
AGGTCCCCGGGAAAAA

Eu gostaria que o seguinte fosse a saída:

>header1_some_extra_data_here
ATGCGGGTACCCCA
>code/header2_some_extra_data
AGGTCCCCGGGAAAAA

Respostas

7 Kusalananda Oct 02 2020 at 19:30

Suas sedsubstituições não funcionarão conforme o esperado porque você nunca será capaz de corresponder a uma nova linha nos dados de entrada. Isso ocorre porque sedlê seu arquivo linha por linha, ou seja, com as novas linhas como delimitadores, e as expressões são aplicadas às linhas individualmente, sem as novas linhas delimitadoras.

Em vez disso, alterando ligeiramente o seu código:

for fasta in ./*.fa; do
    sed 's;^\(>.*\)/.\{0,10\}$;\1;' "$fasta"
done

As poucas mudanças que fiz são:

  1. Use ;como delimitador para o s///comando em vez do padrão /. Isso nos permite não escapar /do padrão. Quase qualquer caractere pode ser usado como delimitador, mas provavelmente deve-se escolher um que não ocorra no padrão ou no texto de substituição.
  2. Use apenas a sintaxe de expressão regular básica padrão. Em seu padrão, (...)é a sintaxe de expressão regular estendida e \{...\}é a sintaxe de expressão regular básica. Decidi usar a sintaxe básica para portabilidade. Isso também significa descartar a -ropção que ativa a sintaxe estendida no GNU sed.
  3. Ancorar o padrão no início e no final da linha com ^e $respectivamente.
  4. Não tente inserir uma nova linha no bit de substituição.

Uma sedexpressão alternativa e mais curta seria

sed '/^>/s;/.\{0,10\}$;;'

Isso aplica uma substituição a todas as linhas que começam com o >caractere ( /^>/atua como o "endereço" para o s///comando subsequente ). A substituição simplesmente exclui o /e o bit após ele no final da linha se esse bit tiver 10 caracteres ou menos.

2 terdon Oct 02 2020 at 19:48

Esta é uma maneira ligeiramente diferente:

  1. Salve a saída em novos arquivos:

    for file in *fa; do 
         sed -E 's|^\s*(>.{10,}.*)/.*|\1|' "$file" > "$file.fixed"; 
     done
    
  2. Edite os arquivos no local:

    sed -i -E 's|^\s*(>.{10,}.*)/.*|\1|' *.fa 
    

A -Eopção ativa expressões regulares estendidas. Isso nos permite usar ()para captura e {}para repetições sem a necessidade de escapar delas. Eu também alterei o delimitador para |para maior clareza e adicionei ^\s*(observe que isso pode não ser compatível com o seu sed; se não for, você pode usar no ^ *lugar), já que às vezes você pode ter um espaço >em branco antes dos arquivos fasta.

Então, o truque é combinar a >seguido por 10 ou mais caracteres até a /, capturar esses caracteres com os parênteses para que eles se tornem \1e substitua a linha inteira com apenas a parte combinada.

Observe que isso encontrará o trecho mais longo de> 10 caracteres até o último /. Portanto, se houver vários /na mesma linha, todos serão mantidos, exceto o último. Por exemplo:

$ echo ">header1_some_extra_data_here/1-1000/foo/bar/baz" | 
    sed -E 's|^\s*(>.{10,}.*)/.*|\1|' 
>header1_some_extra_data_here/1-1000/foo/bar
    

Para evitar isso e remover tudo após o primeiro /, desde que você já tenha correspondido 10 caracteres, use:

sed -E 's|^\s*(>.{10}[^/]*)/.*|\1|'