sed con quantificatori numerici - come fare?

Oct 02 2020

Sto cercando di modificare le intestazioni fasta di più file, al fine di rimuovere una barra e tutto ciò che segue (purché 'tutto dopo' sia uguale o inferiore a 10 caratteri). Le righe di intestazione sono contrassegnate da un ">".

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

Ho anche provato

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

ma non sembra essere migliore. La mia impressione è che sia il quantificatore {, 10} che rompe le cose. Non sono sicuro però. L'aiuto sarebbe molto apprezzato!

Ad esempio, se il seguente era in un file:

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

Vorrei che l'output fosse il seguente:

>header1_some_extra_data_here
ATGCGGGTACCCCA
>code/header2_some_extra_data
AGGTCCCCGGGAAAAA

Risposte

7 Kusalananda Oct 02 2020 at 19:30

Le tue sedsostituzioni non funzioneranno come previsto perché non sarai mai in grado di abbinare una nuova riga nei dati di input. Questo perché sedlegge il tuo file riga per riga, cioè con i caratteri di ritorno a capo come delimitatori, e le espressioni vengono applicate alle righe individualmente, senza i caratteri di delimitazione.

Invece, cambiando leggermente il codice:

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

Le poche modifiche che ho fatto sono:

  1. Utilizzare ;come delimitatore per il s///comando invece di quello predefinito /. Questo ci permette di non sfuggire /allo schema. Quasi tutti i caratteri possono essere usati come delimitatori, ma probabilmente si dovrebbe sceglierne uno che non si trova nel modello o nel testo sostitutivo.
  2. Utilizzare solo la sintassi delle espressioni regolari di base standard. Nel tuo modello, (...)è la sintassi delle espressioni regolari estesa ed \{...\}è la sintassi delle espressioni regolari di base. Ho deciso di utilizzare la sintassi di base per la portabilità. Ciò significa anche eliminare l' -ropzione che abilita la sintassi estesa in GNU sed.
  3. Ancorare il motivo all'inizio e alla fine della linea rispettivamente con ^e $.
  4. Non provare a inserire una nuova riga nel bit di sostituzione.

Un'espressione alternativa e più breve sedsarebbe

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

Questo applica una sostituzione a tutte le righe che iniziano con il >carattere ( /^>/funge da "indirizzo" per il s///comando successivo ). La sostituzione cancella semplicemente il /e il bit dopo di esso alla fine della riga se quel bit è lungo 10 caratteri o meno.

2 terdon Oct 02 2020 at 19:48

Ecco un modo leggermente diverso:

  1. Salva l'output in nuovi file:

    for file in *fa; do 
         sed -E 's|^\s*(>.{10,}.*)/.*|\1|' "$file" > "$file.fixed"; 
     done
    
  2. Modifica i file sul posto:

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

L' -Eopzione abilita le espressioni regolari estese. Questo ci consente di utilizzare ()per la cattura e {}per le ripetizioni senza bisogno di sfuggirle. Ho anche cambiato il delimitatore in |per chiarezza e aggiunto ^\s*(nota che questo potrebbe non essere supportato dal tuo sed; se non lo è, puoi usarlo ^ *invece) poiché a volte puoi avere spazi bianchi prima dei >file in fasta.

Quindi, il trucco è abbinare a >seguito da 10 o più caratteri fino a /, catturare questi caratteri con le parentesi in modo che diventino \1e sostituire l'intera riga con solo la parte abbinata.

Nota che questo troverà il tratto più lungo di> 10 caratteri fino all'ultimo /. Quindi, se ce ne sono più /sulla stessa riga, verranno mantenuti tutti tranne l'ultimo. Per esempio:

$ 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
    

Per evitarlo e rimuovere tutto dopo il primo /purché abbiate già trovato 10 caratteri, usate:

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