sed con cuantificadores numéricos - ¿cómo?

Oct 02 2020

Estoy tratando de editar los encabezados fasta de varios archivos para eliminar una barra inclinada y todo lo que sigue (siempre que "todo lo que sigue" sea igual o inferior a 10 caracteres). Las líneas de encabezado están marcadas con un '>'.

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

También lo he intentado

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

pero no parece ser mejor. Mi corazonada es que es el cuantificador {, 10} el que rompe las cosas. Aunque no estoy seguro. ¡La ayuda será muy apreciada!

Por ejemplo, si lo siguiente estaba en un archivo:

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

Me gustaría que el siguiente fuera el resultado:

>header1_some_extra_data_here
ATGCGGGTACCCCA
>code/header2_some_extra_data
AGGTCCCCGGGAAAAA

Respuestas

7 Kusalananda Oct 02 2020 at 19:30

Sus sedsustituciones no funcionarán como se esperaba porque nunca podrá hacer coincidir una nueva línea en los datos de entrada. Esto se debe a que sedlee su archivo línea por línea, es decir, con las líneas nuevas como delimitadores, y las expresiones se aplican a las líneas individualmente, sin las líneas nuevas delimitadores.

En cambio, cambiando ligeramente su código:

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

Los pocos cambios que he realizado son:

  1. Úselo ;como delimitador para el s///comando en lugar del predeterminado /. Esto nos permite no escapar /del patrón. Se puede usar casi cualquier carácter como delimitador, pero probablemente se debería elegir uno que no aparezca en el patrón o en el texto de reemplazo.
  2. Utilice solo la sintaxis de expresión regular básica estándar. En su patrón, (...)es una sintaxis de expresión regular extendida y \{...\}es una sintaxis de expresión regular básica. Me decidí por usar la sintaxis básica para la portabilidad. Esto también significa eliminar la -ropción que habilita la sintaxis extendida en GNU sed.
  3. Anclar el patrón al principio y al final de la línea con ^y $respectivamente.
  4. No intente insertar una nueva línea en la broca de reemplazo.

Una sedexpresión alternativa y más corta sería

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

Esto aplica una sustitución a todas las líneas que comienzan con el >carácter ( /^>/actúa como la "dirección" para el s///comando posterior ). La sustitución simplemente elimina el /bit y el bit que sigue al final de la línea si ese bit tiene 10 caracteres o menos.

2 terdon Oct 02 2020 at 19:48

He aquí una forma ligeramente diferente:

  1. Guarde la salida en archivos nuevos:

    for file in *fa; do 
         sed -E 's|^\s*(>.{10,}.*)/.*|\1|' "$file" > "$file.fixed"; 
     done
    
  2. Edite los archivos en su lugar:

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

La -Eopción habilita expresiones regulares extendidas. Esto nos permite usar ()para capturar y {}para repeticiones sin necesidad de escapar de ellas. También cambié el delimitador a |para mayor claridad y agregué ^\s*(tenga en cuenta que es posible que esto no sea compatible con su sed; si no lo es, puede usarlo ^ *) ya que a veces puede tener espacios >en blanco antes de los archivos en fasta.

Luego, el truco consiste en hacer coincidir a >seguido de 10 o más caracteres hasta que a /, capture estos caracteres con los paréntesis para que se conviertan en \1y reemplace toda la línea con solo la parte correspondiente.

Tenga en cuenta que esto encontrará el tramo más largo de> 10 caracteres hasta el último /. Entonces, si hay varios /en la misma línea, se mantendrán todos menos el último. Por ejemplo:

$ 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 eso, y eliminar todo después del primero /, siempre que ya haya coincidido con 10 caracteres, use:

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