Shell: Parte 3¶
Wildcards, Parte 2¶
Le wildcard piu’ importanti sono:
wildcard | fa match con |
---|---|
akz |
il testo “akz “ |
* |
una stringa qualunque (anche vuota) |
? |
un carattere qualunque |
[akz] |
un carattere solo tra a , k e z |
[a-z] |
un carattere alfabetico qualunque |
[0-9] |
una cifra qualunque |
[!1b] |
un carattere qualunque che non sia 1 o b |
[!a-e] |
un carattere qualunque che non sia a , b , ..., e |
Quando la shell incontra un comando dove uno (o piu’) degli argomenti contiene delle wildacrds, esegue la wildcard expansion: sostituisce all’argomento incriminato tutti i file che fanno match con la wildcard.
Avvertimento
Le wildcards sono simili alle regex, ma non sono la stessa cosa:
- Le wildcards sono usate dalla shell per fare il match di percorsi.
- Le regex sono usate da
grep
per fare il match di righe di testo contenute in un file. - Le regole che determinano il match di wildcards e regex sono diverse.
Esempio. La wildcard:
le rose sono *se
fa match con:
le rose sono rosse
ma anche con:
le rose sono costose
e:
le rose sono grosse
ma non con:
i maneggi abitano in montagna
Le wildcard possono essere combinate, ad esempio:
test?[a-z][!0-9]
fa il match con tutti i percorsi che cominciano con test
, proseguono con
un carattere qualunque, poi con un carattere alfabetico ed infine con un
carattere non numerico.
Esempio. Un esempio piu’ realistico. Il comando:
cat data/dna-fasta/*.[12]
fa match con tutti i file nella directory data/dna-fasta
il cui filename
e’ composto di una-stringa-qualunque, seguita da un punto, seguito da 1
o
2
e nient’altro. Nel nostro caso i soli file a fare match sono:
data/dna-fasta/fasta.1
data/dna-fasta/fasta.2
Dopo la wildcard expansion il comando precedente diventa:
cat data/dna-fasta/fasta.1 data/dna-fasta/fasta.2
Esempio. Per stampare a schermo i contenuti della directory data, scrivo:
ls data
Per stampare i contenuti delle directory che stanno in data:
ls data/*
qui la wildcard *
viene espansa in:
aatable deep0 ... deep4 dna-fasta empty1 empty2 prot-fasta prot-pdb simple1
Per stampare a schermo solo il contenuto delle directory deep0
, ..., deep4
:
ls data/deep*
Mentre per restringere la wildcard alle directory deep0
e deep3
:
ls data/deep[03]
e solo per le directory deep0
, ..., deep4
ma non deep2
:
ls data/deep[!2]
Esercizi¶
- Cosa fa il comando:
echo *
?echo '*'
?cat data/simple1/*.txt
?
Stampare il contenuto dei file
.txt
indata/simple1
.Stampare il contenuto dei file
.abc
indata/simple1
.Concatenare il contenuto dei file
.txt
indata/simple1
in un nuovo filetemp
.Concatenare il contenuto dei file
.abc
indata/simple1
ed aggiungerlo in coda atemp
.- Tra i file in
/usr/bin
, trovare conls
quelli che: - Iniziano per una cifra.
- Iniziano e finiscono per
x
. - Iniziano o finiscono per
x
.
- Tra i file in
Filtri¶
La shell mette a disposizione un numero di comandi che agiscono da filtri: il loro scopo e’ permettervi di estrarre righe/colonne da file di testo, ordinare i dati, sostituire caratteri, ed in generale calcolare statistiche sui dati.
comando | funzione | opzioni principali |
---|---|---|
wc | conta caratteri, parole e righe | -m , -w , -l |
sort | ordina righe | -f , -n`, ``-r |
uniq | rimuove righe consecutive identiche | -c , -d |
cut | stampa una o piu’ colonne | -d , -f |
tr | traduce caratteri | -d , -s |
grep | seleziona righe in base ad una regex | -E , -i , -v |
Vediamoli all’opera.
Esempio. Creo un file con due righe di testo:
echo uno > file; echo due >> file
Controllo quante righe ho scritto:
wc -l file
Esempio. Per contare il numero di righe di data/numers.1
:
wc -l data/numbers.1
Per contare quanti file e directory ci sono nella home:
ls ~ | wc -l
Esempio. Concateniamo i file in data/dna-fasta
:
cat data/dna-fasta/* > all_fastas
Vogliamo stampare le righe di all_fastas
in ordine alfanumerico:
sort all_fastas
Ed ora vogliamo copiarle in un file:
sort all_fastas > sorted_fastas
Possiamo ottenere lo stesso effetto con una pipeline:
cat data/dna-fasta/* | sort > sorted_fastas
Esempio. I file data/numbers.1
e data/numbers.2
contengono liste
di numeri. Vogliamo controllare se ci sono doppioni usando uniq
.
C’e’ un problema: uniq
trova solo doppioni sequenziali. Se lo applico
a questo testo:
aaaa
bbbb
bbbb
aaaa
uniq
riesce si’ a capire che bbbb
e’ ripetuto, ma non ci riesce con
aaaa
.
Quindi se voglio trovare tutti i doppioni indipendentemente dall’ordine in
cui si trovano nel file che mi interessa, devo prima usare sort
per
avvicinare le righe identiche. Nel nostro caso, faccio:
sort data/numbers.1 > temp1
sort data/numbers.2 > temp2
uniq -d temp1
uniq -d temp2
Non ci sono ripetizioni nei singoli file. Ci sono forse doppioni nei file presi assieme?
Siamo tentati di usare:
cat temp[12] | uniq -d
Pero’ non e’ detto che il concatenamento di due file ordinati produca un file
ordinato. Quindi usamo di nuovo sort
:
cat temp[12] | sort | uniq -d
Il numero 3
appare piu’ volte! Quante? Verifichiamo:
cat temp[12] | sort | uniq -d -c
Due volte. In alternativa:
cat temp[12] | sort | uniq -d | wc -l
Provate a ripetere il codice costruendo incrementalemente la pipeline.
Esempio. Il comando tr
permette di sostituire (tradurre) caratteri con
altri caratteri. Data una sequenza nucleotidica voglio sostituire tutte le
timine T
con uracile U
, scrivo:
echo TATAAA | tr 'T' 'E'
Tra gli usi piu’ comuni di tr
:
Tradurre da maisucolo a minuscolo (e viceversa):
echo 'voglio diventare grande!' | tr 'a-z' 'A-Z'
Sostituire i caratteri (nascosti) “a capo”,
\n
, con altri. Confrontate:ls data
e:
ls data | tr '\n' ','
Rimuovere ripetizioni di caratteri, con l’opzione
-s
(squeeze):echo 'voglio uno spazio solo!' | tr -s ' '
Rimuovere caratteri estranei con
-d
, ad esempio da una sequenza proteica:echo 'xixxxox sxxxonxxo uxxxxnaxx xxxxproxxxtxexxinxa' | tr -d ' '
Esempio. Il comando cut
estrae colonne (non righe!) dallo stdin
.
Ha due opzioni fondamentali (e poco opzionali):
-d
specifica il separatore: il carattere che separa le colonne.-f
specifica quali colonne (fields, campi) estrarre.
Ad esempio, assumete di avere un file con questo testo:
nome cognome anno-di-nascita
Marco Rossi 1989
Luisa Bianchi 1981
Dante Alighieri 1265
Qui le colonne sono separate da semplici spazi. Posso estrarre la colonna dei nomi con:
cut -d' ' -f1 file
e quella delle date con:
cut -d' ' -f3 file
Se volessi estrarre solo i nomi saltando la prima riga:
tail -n +2 file | cut -d' ' -f1
oppure:
cut -d' ' -f1 file | tail -n +2
o ancora:
cat file | cut ... | tail ...
Assumete che il file contenga:
nome,cognome,anno-di-nasciata,impatto-sui-posteri
Dante,Alighieri,1265,9
Marcel,Proust,1871,7
Homer,Simpson,1989,10
Qui la virgola funge da separatore. Per estrarre i cognomi, scrivo:
tail -n +2 file | cut -d',' -f2
Possiamo anche estrarre piu’ di una colonna, ad esempio nome, cognome e data di nascita:
tail -n +2 file | cut -d',' -f1,2,3
oppure:
tail -n +2 file | cut -d',' -f1-3
Per estrarre nome, cognome e impatto:
tail -n +2 file | cut -d',' -f1,2,4
Per estrarre tutte le colonne dall’anno in poi:
tail -n +2 file | cut -d',' -f3-
Esercizi¶
- Confronta
wc A
ecat A | wc
. - Confronta
wc -l A
ecat A | tr '\n' ' ' | wc -w
. - Quanti file sono contenuti in
/usr/bin/
? - Stampare i file in
/usr/bin
ordinati per dimensione, sia conls
da solo che conls | sort ...
. - Stampare solo il file piu’ piccolo in
/usr/bin
. - Stampare i numeri in
data/numbers.1
edata/numbers.2
ordinati dal piu’ piccolo al piu’ grande. - Stampare i numeri in
data/numbers.1
edata/numbers.2
ordinati dal piu’ grande al piu’ piccolo. - Ci sono file doppi in
/usr/bin
? - Scrivere in
listaN.txt
la lista di tutti i file indata/deepN
, per N=1,2,3. - Scrivere in
dataN.txt
i contenuti di tutti i file indata/deepN
, per N=1,2,3. - Quante repliche dispari di
KrustyIlKlown
ci sono indata/deep1
? - Cosa fa
echo ACAB | cut -dC -f2
? Eecho BACA | cut -dA -f1,2
? - Compara
wc -m A
ecat A | wc | tr -s ' ' | cut -d' ' -f4
- Stampa i file in
/usr/bin
ordinati per proprietario. (Si veda-k
nel manuale disort
). - Come sopra, ma in ordine inverso. E’ necessario
tac
? - Stampare solo la dimensione del file piu’ piccolo in
/usr/bin
. - Stampare solo il nome del file piu’ grande in
/usr/bin
. - Ci sono file di dimensioni identiche in
/usr/bin
? Quanti?
Espressioni regolari e grep
¶
grep
serve per estrarre righe che combaciano con una data espressione
regolare: viene usato spesso per filtrare le righe di un file (o dello
stdin
) alle quali siamo interessati, scartando tutte le altre.
La sintassi e’:
grep regex file
oppure:
cat file | grep regex
Una lista (non esaustiva) delle regex estese:
Simbolo | Fa match con |
---|---|
testo |
La stringa testo |
. |
Un carattere qualunque |
[abc] |
Un carattere qualunque tra a , b e c |
[a-z] |
Un carattere nell’intervallo a , ..., z |
[^abc] |
Un carattere che non sia a , b , o c |
$ |
La fine della riga (il carattere \n ) |
^ |
L’inizio della riga (il carattere dopo \n ) |
regex* |
almeno zero ripetizioni di regex |
regex+ |
almeno una ripetizione di regex |
regex{n} |
n ripetizioni di regex |
regex{n,m} |
tra le n e le m ripetizioni di regex |
(regex1|regex2) |
regex1 oppure regex2 |
Avvertimento
Spesso useremo regex estese. Per fare in modo che grep
le riconosca,
useremo l’opzione -E
.
Avvertimento
Le regex hanno alcuni caratteri speciali in comune con le wildcards.
Percio’ quando invochiamo grep
, e’ necessario inibire i caratteri
speciali della regex usata, in modo che la shell non esegua la wildcard
expansion.
Il modo piu’ semplice di farlo e’ usare virgolette:
grep 'regex' file
oppure:
cat file | grep 'regex'
Esempio. Le seguenti regex combaciano con:
.*
, tutte le stringhe (anche la stringa vuota)..+
, tutte le stringhe (ma non quella vuota).abc
, tutte le stringhe che contengonoabc
.[abc]
, tutte le stringhe che contengono almeno una traa
,b
, ec
.^abc
, tutte le stringhe che iniziano perabc
.abc$
, tutte le stringhe che finiscono perabc
.^abc$
, la sola stringaabc
.^.*$
, tutte le stringhe terminate da un carattere di a capo\n
.[a-z]
, tutte le stringhe che contengono almeno un carattere alfabetico minuscolo.^[A-Z ]$
, tutte le stringhe che contengono solo caratteri alfabetici maiuscoli e spazi.^[01 ]{3+}$
, tutte le stringhe di almeno tre caratteri che rappresentano parole binarie.ant(onio|idiluviano)
, tutte stringhe che contengonoantonio
oantidiluviano
(o entrambi).^[ ,](X{10}|Y{10})[ ,]
, tutte le stringhe che iniziano con uno spazio o una virgola, seguito da dieciX
o dieciY
, seguiti da uno spazio o una virgola.
Esempio. Per costruire regex che facciano match con caratteri speciali
(ad esempio il punto .
), posso usare l’escaping o inserirli tra parentesi
quadre. Ad esempio:
grep '.' data/aatable
estrae “tutte le righe che contengono almeno un carattere”, mentre:
grep '\.' data/aatable
grep '[.]' data/aatable
estraggono “tutte le righe che contengono un punto”.
Nota
L’opzione --color
chiede a grep
di colorare il match.
Esempio. Prendiamo il file 1A34.fasta
da qui.
Contiene la sequenza aminoacidica delle tre catene della proteina 1A34:
>1A34:A|PDBID|CHAIN|SEQUENCE
MGRGKVKPNRKSTGDNSNVVTMIRAGSYPKVNPTPT
WVRAIPFEVSVQSGIAFKVPVGSLFSANFRTDSFTS
VTVMSVRAWTQLTPPVNEYSFVRLKPLFKTGDSTEE
FEGRASNINTRASVGYRIPTNLRQNTVAADNVCEVR
SNCRQVALVISCCFN
>1A34:B|PDBID|CHAIN|SEQUENCE
AAAAAAAAAA
>1A34:C|PDBID|CHAIN|SEQUENCE
UUUUUUUUUU
Ogni catena compare come un’intestazione (o header) che comincia col
carattere >
, seguita dalla sequenza vera e propria.
Per estrarre le intestazioni, sfrutto il fatto che cominciano per >
:
grep '>' 1A34.fasta
Il risultato e’:
>1A34:A|PDBID|CHAIN|SEQUENCE
>1A34:B|PDBID|CHAIN|SEQUENCE
>1A34:C|PDBID|CHAIN|SEQUENCE
Ancora meglio, costringo grep
a cercare >
all’inizio della riga (e non,
ad esempio, nel bel mezzo di una sequenza proteica):
grep '^>' 1A34.fasta
Per estrarre invece le sequenze:
grep -v '^[^>]' 1A34.fasta
Esempio. Se siamo interessati a scoprire se, tra le catene in
data/prot-fasta/
quali contengono la sequenza “DP
” (leggi: acido
aspartico seguito da prolina):
grep DP data/prot-fasta/*.fasta
Il risultato e’:
data/prot-fasta/3J00.fasta:FVIDADHEHIAIKEANNLGIPV...
data/prot-fasta/3J00.fasta:PRRRVIGQRKILPDPKFGSELL...
data/prot-fasta/3J00.fasta:SMQDPIADMLTRIRNGQAANKA...
data/prot-fasta/3J01.fasta:AKGIREKIKLVSSAGTGHFYTT...
data/prot-fasta/3J01.fasta:EYDPNRSANIALVLYKDGERRY...
data/prot-fasta/3J01.fasta:ARNLHKVDVRDATGIDPVSLIA...
Quindi si’, abbiamo risposto alla nostra domanda: ci sono ben due proteine
(3F00
e 3J01
) che includono il pattern “DP
”.
Per controllare di non avere mai fatto match con le intestazioni, scrivo:
grep DP data/prot-fasta/*.fasta | grep '^>'
grep
non stampa niente, percio’ non abbiamo mai tenuto intestazioni. Bene.
Per evitare sorprese, possiamo filtrare via le intestazioni a priori con:
grep -v DP data/prot-fasta/*.fasta | grep -v '^>'
Esempio. Costruiamo una pipeline complessa.
Dato l’output di grep DP ...
, voglio stampare il nome delle proteine che
contengono la sequenza DP
. L’output di grep
era:
data/prot-fasta/3J00.fasta:FVIDADHEHIAIKEANNLGIPV...
data/prot-fasta/3J00.fasta:PRRRVIGQRKILPDPKFGSELL...
data/prot-fasta/3J00.fasta:SMQDPIADMLTRIRNGQAANKA...
data/prot-fasta/3J01.fasta:AKGIREKIKLVSSAGTGHFYTT...
data/prot-fasta/3J01.fasta:EYDPNRSANIALVLYKDGERRY...
data/prot-fasta/3J01.fasta:ARNLHKVDVRDATGIDPVSLIA...
Usiamo cut
per tagliare la colonna dei nomi dei file (che contiene 3J00
e 3J01
). Come prima cosa, uniformiamo i delimitatori con tr
:
grep DP data/prot-fasta/*.fasta | tr '/.' ' '
ottenendo:
data prot-fasta 3J00 fasta:FVIDADHEHIAIKEANNLGIPV...
data prot-fasta 3J00 fasta:PRRRVIGQRKILPDPKFGSELL...
data prot-fasta 3J00 fasta:SMQDPIADMLTRIRNGQAANKA...
data prot-fasta 3J01 fasta:AKGIREKIKLVSSAGTGHFYTT...
data prot-fasta 3J01 fasta:EYDPNRSANIALVLYKDGERRY...
data prot-fasta 3J01 fasta:ARNLHKVDVRDATGIDPVSLIA...
Ora uso cut
per tenere solo la colonna giusta:
grep DP data/prot-fasta/*.fasta | tr './' ' ' | cut -d' ' -f3
ottenendo:
3J00
3J00
3J00
3J01
3J01
3J01
A questo punto uso sort
e uniq
per rimuovere le ripetizioni:
grep ... | tr '/.' ' ' | cut -d' ' -f3 | sort | uniq
ottenendo:
3J00
3J01
In alterantiva posso invocare cut
due volte:
grep DP ... | cut -d'/' -f3 | cut -d'.' -f1 | ...
Nota
Ci sono molti comandi utilissimi che non fanno parte del corso. Per chi fosse interessato, una lista dei piu’ importanti:
paste
, il contrario dicut
: concatena colonne orizzontalmente.rev
, stampa una riga dalla fine all’inizio.sed
, permette di rimuovere o sostituire intere stringhe – untr
molto piu’ potenteawk
, permette manipolazioni arbitrariamente complesse, ad esempio di fare aritmeticabc
, un calcolatore- e molti, molti altri ancora...
Esercizi¶
- Cosa significano le seguenti regex (se valide)?
.
.*
[09]{2}
[0-9]{2}
*
[
[[]
^.3
^.{3}
.{3}$
^>
AA
^AA$
aA
[aA]
word
w..d
^$
[}{]
[0-9]+
- Scrivere una regex che facciano match con:
- Tutti i caratteri alfanumerici, maiuscoli e minuscoli
- Le righe contenenti solo spazi
- Le righe che contengono punti esclamativi o punti interrogativi
- I giorni della settimana (nel modo piu’ compatto possibile) Senza lettere accentate. La shell non le digerisce.
- Le parole di radice
frazion
-, ad esempio: frazione, frazionario, etc. - I multipli di 10, i multipli di 5, i numeri dispari
- I numeri razionali come frazione, ad esempio: p/q
- I numeri razionali in notazione decimale, ad esempio: 1.34, .99, 17., 3
- I numeri razionali in notazione scientifica, ad esempio: 1.34e10, 1.34e-10
- Le somme (esempio: a+b+c+d, a+b, etc.) di lunghezza arbitraria, dove a, b, c, ... sono numeri interi
- Le somme di due moltiplicazioni, cose come: (2 * 3 * 2) + (5 * 7), (6 * 2) + (4 * 3), etc.
Nota
Nei prossimi esercizi faremo uso di questo file sequences.fasta
:
Gli esercizi chiedono di contare quante sequenze nel file FASTA contengono (o meno) contengono un motivo data. Il motivo puo’ essere naturalmente espresso da una (o piu’) regex.
- Quando scrivo “residuo1; residuo2; residuo3” intendo “residuo1; seguito nella sequenza da residuo2; seguito da residuo3”. Ometto i vari “seguito da” per compattezza.
- Quando scrivo “un aminoacido qualunque”, intendo un carattere qualunque, per semplicita’.
Per contare quante sequenze, si consiglia l’uso di wc
in pipe con
grep
.
Calcolare quante sequenze contengono i seguenti motivi:
- Una fenilalanina (F); due aminoacidi arbitrari; un’altra fenilalanina. Il motivo deve apparire alla fine della sequenza.
- Una arginina (R); una fenilalanina; un aminoacido che non e’ una prolina (P); una isoleucina (I) oppure una valina (V).
Calcolare inoltre quante sequenze includono almeno uno dei due motivi.
Calcolare quante sequenze contengono i seguenti motivi:
- Tre tirosine (Y); al piu’ tre amino acidi qualunque; una istidina (H).
- Un aminoacido non standard o un residuo sconosciuto X.
Ci sono sequenze che soddisfano entrambe le condizioni?
Nota
Gli aminoacidi standard sono: A R N D C E Q G H I L K M F P S T W Y V.
Calcolare quante sequenze contengono i seguenti motivi:
- Un arginina (R); una lisina (K). Il motivo non deve apparire all’inizio della sequenza.
- Due arginine seguite da un amino acido che non sia ne’ una arginina, ne’ una lisina.
- Nessuno dei due motivi precedenti.
Clacolare quante sequenze contengono i seguenti motivi:
- Una fenilalanina (F); un aminoacido qualunque; una fenilalanina o una tirosina (Y); una prolina (P).
- Una prolina; una treonina (T) o una serina (S); una alanina (A); un’altra prolina. Il motivo non deve apparire ne’ all’inizio, ne’ alla fine della sequenza.
- Il primo motivo seguito dal secondo, oppure il secondo seguito dal primo.