# Praca z plikami tekstowymi w linii komend

[← Spis treści](https://ggoralski.gitbook.io/filogenetyka/readme)

## Praca z plikami tekstowymi w linii komend

### Dlaczego to takie ważne?

Jak już wspominałem, praca bioinformatyka to w dużej mierze, o ile nie przede wszystkim praca z plikami tekstowymi. W [poprzedniej lekcji](https://ggoralski.gitbook.io/filogenetyka/cwiczenia/11-praca_w_linuksie) pokazałem jak umieścić tekst w pliku przy pomocy operatorów `>` oraz `>>` a także jak wyświetlić zawartość pliku używając komend `more`, `less` i `cat`. Linux oferuje jednak znacznie więcej narzędzi pozwalających na efektywną pracę z plikami tekstowymi bez uciekania się do pomocy edytorów tekstu (na które też przyjdzie pora). Poniżej pokażę wybrane możliwości niektórych z nich. Warto jednak samodzielnie poszerzyć wiedzę na ten temat, ponieważ potrafią znacznie usprawnić i przyspieszyć pracę.

W terminalu utwórz katalog `Pliki_tekstowe` i wejdź do niego. Używając polecenia `curl` lub `wget` pobierz plik:

```
http://ggoralski.pl/files/filogenetyka-data/Orobanchaceae-trnL-trnF-aligned.fasta
```

### Liczenie zawartości i wyświetlanie części pliku - polecenia `wc`, `head`, `tail`.

Wykonaj polecenie:

```
$: wc Orobanchaceae-trnL-trnF-aligned.fasta 
  18   18 9254 Orobanchaceae-trnL-trnF-aligned.fasta
```

Komenda `wc` wyświetla liczbę linii, słów oraz wielkość liczoną w bajtach podanego pliku. Podając odpowiednie flagi, można uzyskać tylko wybrane dane (sprawdź `wc --help`), co później wykorzystamy. W przypadku pobranego pliku liczba linii i słów jest taka sama (18) ponieważ w opisach i sekwencjach nie występują spacje a sekwencje zostały umieszczone każda w jednej linii. Sprawdź zatem inny plik:

```
http://ggoralski.pl/files/filogenetyka-data/Orobanchaceae-trnL-trnF-aligned.aln
```

Zawiera on dopasowane sekwencje zapisane w formacie `CLUSTAL`. Zobacz jaka jest zawartość pliku a następnie sprawdź ile ma linii i słów.

Polecenie `wc` może odczytywać dane z wielu plików jednocześnie:

```
$: wc *
  233   490 17672 Orobanchaceae-trnL-trnF-aligned.aln
   18    18  9254 Orobanchaceae-trnL-trnF-aligned.fasta
  251   508 26926 razem
```

Nie zawsze konieczne jest przeglądanie całego pliku. Czasem chcemy po prostu zobaczyć jak wygląda jego początek i koniec. Wtedy przychodzą nam z pomocą polecenia `head` i `tail`, które jak łatwo zgadnąć wyświetlają odpowiednio początek i koniec pliku:

```
$: head Orobanchaceae-trnL-trnF-aligned.aln 
CLUSTAL 2.1 multiple sequence alignment


KY484464_O._teucrii                AGTTATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAAGAGAAA-GGGC
KY484493_O._flava                  AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAAGAAAAA-GGGC
KY484489_O._mayeri                 AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAATAAAAA-GGGC
KY484471_O._kochii                 AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAATAAAAA-GGGC
KY484474_O._elatior                AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAATAAAAA-GGGC
KU238865_O._coerulescens           AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAAGAAAAA-GGGC
KY484502_P._ramosa                 GGTGATAAC-TTTCAAATTCAGAGAAACTCCGGAATTAATAAAAACGGGC
$: tail Orobanchaceae-trnL-trnF-aligned.aln 
KY484464_O._teucrii                AAATG
KY484493_O._flava                  AAATG
KY484489_O._mayeri                 AAATG
KY484471_O._kochii                 AAATG
KY484474_O._elatior                AAATG
KU238865_O._coerulescens           GAATG
KY484502_P._ramosa                 GAATG
KY484503_P._purpurea               GAATG
KX524675_Lindenbergia_siniaca      GAATG
                                    ****
```

Liczbę wyświetlanych linii można dostosować używając flagi `-n`:

```
$: head -n 5 Orobanchaceae-trnL-trnF-aligned.aln 
CLUSTAL 2.1 multiple sequence alignment


KY484464_O._teucrii                AGTTATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAAGAGAAA-GGGC
KY484493_O._flava                  AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAAGAAAAA-GGGC
$: tail -n 5 Orobanchaceae-trnL-trnF-aligned.aln 
KU238865_O._coerulescens           GAATG
KY484502_P._ramosa                 GAATG
KY484503_P._purpurea               GAATG
KX524675_Lindenbergia_siniaca      GAATG
                                    ****
```

### `grep` - wyszukiwanie w pliku tekstowym

Narzędzie `grep` z pewnością należy do najbardziej użytecznych programów do pracy z plikami tekstowymi. Służy do wynajdywania w plikach danego ciągu znaków.

W katalogu z pobranymi (patrz wyżej) plikami wykonaj polecenie:

```
$: grep Lindenbergia Orobanchaceae-trnL-trnF-aligned.fasta 
>KX524675_Lindenbergia_siniaca
```

W podanym pliku został znaleziony ciąg znaków `Lindenbergia` a następnie linia w którym się znajdował została wyświetlona. Jeśli znaleziony fragment nie wyświetla się w innym kolorze niż pozostała część linii to można to uzyskać podając opcję `--color=AUTO`, np. `grep --color=AUTO Lindenbergia Orobanchaceae-trnL-trnF-aligned.fasta`.

Można przeszukać od razu wiele plików np. używając znaków wieloznacznych, uzyskamy także informację w jakim pliku znaleziono dopasowanie:

```
$: grep Lindenbergia *          

Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      AGTGATAAC-TTTCAAATTCAGAGAAACCCCGGAATTAAAAAAGG-GGGC
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      AATCCTGAGCCAAATCCTGT-----TTTCTCAAAACAAA-G--------A
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      C-AAAAATAAAGGATAGGTGCAGAGACTCAACGGAAGCTGTTCTAAC---
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      -AAATGGAGTTGATTGCGCCGGTAGAGGAATCTTTCCATCGAAACTTCGG
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      AAAGGATGAAGGATAAACGTATCTATTGAATACTATATCAAATTATTAAT
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      GATATCAAATTATTAATGATGGCCCGAATCTGTATCTGTATTTTT---TA
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      ATTTTAATATGAAAAATGGAAAAGTTAGTGTGAATTGATTCCATATTGAA
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      GAAAGAATCGAATATTCATT-------CATCAAATCATTCACTCCACAGT
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      CCGATAGATC-----TTTTAAAGAATTGATTAATCGGATGAGAATAAAGA
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      TAGAGTCCCATTC----------TACATGTCAATACCGGCAACAATGAAA
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      TTTATAGTAAGAGGAAAATCCGTCGACTTTAAAAATCGTGAGGGTTCAAG
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      TCCCTCTATCCCCAA---AAAAAGTCTATTTTACTTCCA-----------
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      -----------AAATATTTAGCCTATTTCA-----------------TTT
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      TCG-TTAGCGGTTCCAAATTCCTT--TTCTGATTC--TTTGACA--AACG
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      TATTGGGGCGT------------------AAATGACTTT-CTCTTATCAC
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      ATGTG------ATATAGAATACACATCCAAATTCAGCAAGGAATTCCTAT
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      TTGAATG------ATTCAGAATCAATAACATTACTC-AT-ACTGAAACTT
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      AGA---AAGTTCGTCTTTTTGAAGATCCAATAAATTACAGGATTTGGAGA
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      AAACTTTGTAATCTTCCCCA--TCCCTTTAATTGACATAGAGCCCAGTC-
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      ---ATCTAAT--AAAATCA------GGATGGGAATGGGATTATACATTGG
Orobanchaceae-trnL-trnF-aligned.aln:KX524675_Lindenbergia_siniaca      GAATG
Orobanchaceae-trnL-trnF-aligned.fasta:>KX524675_Lindenbergia_siniaca
```

Jeśli chcemy przeszukać pliki w danym katalogu, należy użyć opcji `-r`, np.: `grep -r Lindenbergia SEKWENCJE`.

Narzędzia grep można użyć na przykład do sprawdzenia ile sekwencji znajduje się w pliku `fasta`. W tym celu należy znaleźć i policzyć linie zawierające znak `>`. Tu jednak czyha pewna pułapka. Najbardziej oczywistą komendą wydaje się: `grep > Orobanchaceae-trnL-trnF-aligned.fasta` ale jej wynik będzie taki:

```
$: grep > Orobanchaceae-trnL-trnF-aligned.fasta 
Składnia: grep [OPCJA]... WZORZEC [PLIK] ...
Napisz „grep --help” żeby dowiedzieć się więcej.
```

Po pierwsze polecenie ewidentnie nie zrobiło tego czego po nim oczekiwaliśmy a po drugie jeśli teraz spróbujemy wyświetlić zawartość pliku `Orobanchaceae-trnL-trnF-aligned.fasta` to okaże się, że jest pusty. Dlaczego?

Przypomnijmy sobie, że operator `>` służy do przekazania tekstu do pliku. Zatem polecenie `grep > Orobanchaceae-trnL-trnF-aligned.fasta` uruchomiło polecenie `grep` (z nieprawidłową składnią) a wynik jego działania zapisało w pliku, w którym chcieliśmy szukać znaków `>`, wymazując jego poprzednią zawartość. Jest to bardzo łatwy do popełnienia błąd, który może się skończyć utratą wyników pracy! Jak więc należy prawidłowo wyszukać znaku `>`? Umieszczając go w cudzysłowach:

```
$: grep ">" Orobanchaceae-trnL-trnF-aligned.fasta
>KY484464_O._teucrii
>KY484493_O._flava
>KY484489_O._mayeri
>KY484471_O._kochii
>KY484474_O._elatior
>KU238865_O._coerulescens
>KY484502_P._ramosa
>KY484503_P._purpurea
>KX524675_Lindenbergia_siniaca
```

> Zadanie: Zastanów się, co trzeba zrobić, żeby uzyskać nazwy sekwencji w osobnym pliku?

No dobrze, teraz możemy policzyć wyświetlone linie ale wolelibyśmy, żeby komputer sam je policzył. Co prawda istnieje odpowiednia opcja programu `grep`, która to robi (znajdź ją), ale jest to dobra okazja, żeby pokazać w jaki sposób można połączyć różne polecenia tak aby wykonały razem zadanie.

### Potoki, czyli łączymy polecenia.

Chcemy policzyć linie, w których znajduje się znak `>`. Mamy do dyspozycji jedno narzędzie, które wyszukuje odpowiednie linie (`grep`) i drugie, które liczy linie (`wc`). Trzeba je zatem odpowiednio połączyć w jeden ,,taśmociąg''. Wykorzystamy do tego potoki (ang. *pipe*) - wynik działania `grep` (strumień) zostanie skierowany do `wc`. Do łączenia w ten sposób poleceń służy operator `|`. Polecenia `wc` użyjemy z flagą `-l`, która zwraca liczbę linii.

```
$: grep ">" Orobanchaceae-trnL-trnF-aligned.fasta | wc -l
9
```

Wiemy, że w pliku mamy 9 sekwencji.

Teraz poznamy kolejną opcję `-v`, która pozwala ,,odwrócić'' dopasowanie, czyli zwrócić linie, w których **nie występuje** dopasowanie i połączymy dwa polecenia `grep` tak, aby uzyskać listę próbek, które **nie zawierają** ciągu ,,Lindenbergia'':

```
$: grep -v Lindenbergia Orobanchaceae-trnL-trnF-aligned.fasta | grep ">"
>KY484464_O._teucrii
>KY484493_O._flava
>KY484489_O._mayeri
>KY484471_O._kochii
>KY484474_O._elatior
>KU238865_O._coerulescens
>KY484502_P._ramosa
>KY484503_P._purpurea
```

Jak widać, w drugim wywołaniu plecenia `grep` nie podaliśmy nazwy pliku, tekst do przeanalizowania został przekazany za pomocą potoku.

Wynik możemy przekazać dalej, na przykład do polecenia `sort`, które jak nazwa wskazuje sortuje linie tekstu:

```
$: grep -v Lindenbergia Orobanchaceae-trnL-trnF-aligned.fasta | grep ">" | sort
>KU238865_O._coerulescens
>KY484464_O._teucrii
>KY484471_O._kochii
>KY484474_O._elatior
>KY484489_O._mayeri
>KY484493_O._flava
>KY484502_P._ramosa
>KY484503_P._purpurea
```

Można oczywiście uzyskaną posortowaną listę nazw sekwencji przekazać do pliku:

```
$: grep -v Lindenbergia Orobanchaceae-trnL-trnF-aligned.fasta | grep ">" | sort > posortowane.txt
```

Wykonaj powyższe polecenie i sprawdź zawartość pliku `posortowane.txt`.

### `xargs` - przekazanie argumentów do kolejnego polecenia

Narzędzie `xargs` pozwala przekazać kolejnemu poleceniu argumenty otrzymane ze standardowego wejścia, czyli np. przez potok. Najłatwiej pokazać to na przykładzie:

```
ls *.fasta | xargs cat > wszystkie.fasta
```

Najpierw mamy polecenie `ls *fasta`, które zwraca listę plików o przedłużeniu `fasta` znajdujących się w bieżącym katalogu. Jest ona przekazywana przez potok do polecenia `xargs`, które przekazuje je poleceniu `cat` jako kolejne argumenty. Jak wiemy polecenie `cat` zwraca zawartość pliku, którego nazwa jest argumentem do standardowego wyjścia, którym może być ekran, ale w tym przypadku, przekierowujemy ją do pliku `wszystkie.fasta`. Tak więc całe polecenie umieszcza zawartość wszystkich plików `fasta` w pliku wynikowym. W tym przypadku można by było po prostu wykonać komendę:

```
cat *.fasta > wszystkie.fasta
```

ale jak się później przekonamy, nie zawsze tak łatwo jest pominąć `xargs` i program ten bywa bardzo pożyteczny.

### `sed` - przetwarzanie tekstu

Kolejne bardzo użyteczne narzędzie to `sed`. Pozwala na edycję strumienia danych. Jedną z zalet używania tego typu programów jest możliwość uzyskiwania i modyfikacji danych z plików bez konieczności ich otwierania od razu w całości, jak to ma miejsce w edytorach tekstu. Nie ma to wielkiego znaczenia w przypadku niewielkich plików ale na przykład pliki uzyskiwane przy sekwencjonowaniu genomów mogą mieć wielkość wielu gigabajtów co w znacznym stopniu utrudnia pracę nad nimi w edytorach tekstu. Nawet jednak dla małych plików praca z linii komend może być po prostu znacznie szybsza.

Prosty schemat użycia `sed` wygląda tak:

```
sed 's/szukany/zmieniony/g' plik
```

W pliku `plik` zmieniane są wszystkie ciągi znaków `szukany` na `zmieniony`. Bez opcji `g` (po trzecim ukośniku) zmienione zostanie tylko pierwsze wystąpienie szukanego ciągu w linii. Zamiast pliku można użyć strumienia danych z innych poleceń.

```
$: echo "ATCTTGGATCG" | sed 's/T/U/g'
AUCUUGGAUCG
```

Kilka przykładów, wykorzystamy utworzony powyżej plik `posortowane.txt`, najpierw sprawdźmy co zawiera:

```
$: more posortowane.txt 

>KU238865_O._coerulescens
>KY484464_O._teucrii
>KY484471_O._kochii
>KY484474_O._elatior
>KY484489_O._mayeri
>KY484493_O._flava
>KY484502_P._ramosa
>KY484503_P._purpurea
```

Zamiana znaków '\_' na spacje:

```
$: sed 's/_/ /g' posortowane.txt

>KU238865 O. coerulescens
>KY484464 O. teucrii
>KY484471 O. kochii
>KY484474 O. elatior
>KY484489 O. mayeri
>KY484493 O. flava
>KY484502 P. ramosa
>KY484503 P. purpurea
```

Usuwane znaku `>`, ponieważ występuje on tylko raz w linii, opcja `g` jest zbędna:

```
$: sed 's/>//' posortowane.txt 

KU238865_O._coerulescens
KY484464_O._teucrii
KY484471_O._kochii
KY484474_O._elatior
KY484489_O._mayeri
KY484493_O._flava
KY484502_P._ramosa
KY484503_P._purpurea
```

Połączmy teraz dwa powyższe i zmieńmy skróty `P.` oraz `O.` na pełne nazwy rodzajowe:

```
$: sed 's/>//' posortowane.txt | sed 's/_/ /g' | sed 's/O\./Orobanche/' | sed 's/P\./Phelipanche/'

KU238865 Orobanche coerulescens
KY484464 Orobanche teucrii
KY484471 Orobanche kochii
KY484474 Orobanche elatior
KY484489 Orobanche mayeri
KY484493 Orobanche flava
KY484502 Phelipanche ramosa
KY484503 Phelipanche purpurea
```

Zauważ, że zamiast napisać `s/O./Orobanche/` napisałem `s/O\./Orobanche/`. Co prawda w tym przypadku oba warianty zwrócą taki sam wynik, ale sposób ich działania jest nieco odmienny, o czym za chwilę.

Wynik działania `sed` często chcemy zapisać w pliku. Można w tym celu użyć znaku `>`:

```
$: sed 's/>//' posortowane.txt | sed 's/_/ /g' | sed 's/O\./Orobanche/' | sed 's/P\./Phelipanche/' > zmieniony.txt
```

Teraz można wykonać polecenie:

```
$: mv zmieniony.txt posortowane.txt
```

Jeśli chcemy bezpośrednio zmienić plik oryginalny, należy użyć opcji -i ale wtedy nie użyjemy potoków, możemy natomiast zgrupować formuły zmian razem oddzielając je średnikami (jeśli to nie działa spróbuj dodać opcję `-e`):

```
$: sed -i 's/>//;s/_/ /g;s/O\./Orobanche/;s/P\./Phelipanche/' posortowane.txt
```

Można też wykonać kolejno operacje:

```
$: sed -i 's/>//' posortowane.txt
$: sed -i 's/_/ /g' posortowane.txt
$: sed -i 's/O\./Orobanche/' posortowane.txt
$: sed -i 's/P\./Phelipanche/' posortowane.txt
```

Należy uważać ze zmianą oryginalnego pliku, lepiej najpierw sprawdzić czy wpisywana formuła edycji działa prawidłowo a dopiero później użyć opcji `-i`. Oczywiście warto też mieć zabezpieczoną kopię oryginalnego pliku.

### Wyrażenia regularne

Wyrażenia regularne są bardzo użyteczne w bioinformatyce, ponieważ są potężnym narzędziem ułatwiającym pracę z plikami tekstowymi i strumieniami tekstu czy ogólniej z ciągami znaków. Dzięki nim można opisać z różnym stopniem ogólności ciągi znaków, czy ich kategorie aby później je wyświetlić, usunąć czy zmodyfikować. Wykorzystuje się je w takich narzędziach jak `grep`, `sed` i wielu innych z dobrymi edytorami tekstu włącznie (np. `vim`).

Przyjrzyjmy się ponownie zmodyfikowanemu plikowi `posortowane.txt`.

```
$: more posortowane.txt 

KU238865 Orobanche coerulescens
KY484464 Orobanche teucrii
KY484471 Orobanche kochii
KY484474 Orobanche elatior
KY484489 Orobanche mayeri
KY484493 Orobanche flava
KY484502 Phelipanche ramosa
KY484503 Phelipanche purpurea
```

Przypuśćmy, że chcemy uzyskać jedynie listę organizmów, bez numerów GenBank-u. Można by je po kolei usuwać, ale znaczne wygodniej będzie użyć wyrażeń regularnych:

```
$: sed 's/^[[:alnum:]]\+[[:space:]]//' posortowane.txt

Orobanche coerulescens
Orobanche teucrii
Orobanche kochii
Orobanche elatior
Orobanche mayeri
Orobanche flava
Phelipanche ramosa
Phelipanche purpurea
```

Zanim wyjaśnię jak i dlaczego to działa, trochę teorii.

W wyrażeniach regularnych możemy stosować znaki rozumiane dosłownie (litery, cyfry, spacje, niektóre inne znaki) oraz znaki specjalne (`$`, `*`, `.`, `[`, `\`, oraz `^`) i wyrażenia, które pozwalają na przykład opisać kategorie znaków, ich liczbę, położenie itp. Na przykład `ATG` oznacza ciąg `ATG` ale `[ATG]` oznacza `A` lub `T` lub `G`. Specjalne znaczenie ma znak `\`, który zmienia znaczenie następnego znaku - na przykład oznacza, że następujący po nim znak specjalny powinien być rozumiany dosłownie albo, że zwykły znak ma znaczenie specjalne.

W poniższej tabeli znajdują się **niektóre** przykłady:

|   **Wyrażenie**   | **Znaczenie**                                                                                       | **Przykład** | **Znaczenie przykładu**                |
| :---------------: | --------------------------------------------------------------------------------------------------- | :----------: | -------------------------------------- |
|        `*`        | zero lub więcej wystąpień wyrażenia                                                                 |     `T*`     | zero lub więcej liter `T`              |
|        `\+`       | jedno lub więcej wystąpień wyrażenia                                                                |     `T\+`    | jedna lub więcej litera `T`            |
|        `\?`       | zero lub jedno wystąpień wyrażenia                                                                  |     `T\?`    | zero lub jedna litera `T`              |
|      `\{i\}`      | dokładnie `i` wystąpień wyrażenia                                                                   |   `T\{3\}`   | trzy znaki `T`                         |
|     `\{i,j\}`     | pomiędzy `i` a `j` wystąpień wyrażenia                                                              |  `T\{3,5\}`  | pomiędzy 3 a 5 znaków `T`              |
|      `\{i,\}`     | `i` lub więcej wystąpień wyrażenia                                                                  |   `T\{3,\}`  | przynajmniej 3 znaki `T`               |
|        `.`        | jakikolwiek znak                                                                                    |   `.\{3\}`   | trzy dowolne znaki                     |
|        `^`        | początek linii                                                                                      |     `^T`     | znak `T` występujący na początku linii |
|        `$`        | koniec linii                                                                                        |     `T$`     | znak `T` na końcu linii                |
|     `[znaki]`     | jakikolwiek znak z zestawu znaków                                                                   |    `[TGA]`   | `T` lub `G` lub `A`                    |
|      `[x-y]`      | jakikolwiek znak z zakresu x do y (litery, cyfry)                                                   |    `[A-Z]`   | duża litera                            |
|                   |                                                                                                     |  `[a-z 0-9]` | mała litera lub cyfra                  |
|     `[^znaki]`    | jakikolwiek znak z poza zestawu znaków                                                              |   `[^TGA]`   | jakikolwiek znak oprócz `T`, `G` i `A` |
| `\znak_specjalny` | znak specjalny (`$`, `*`, `.`, `[`, `\`, `^`) rozumiany dosłownie                                   |     `\^`     | znak `^` (a nie początek linii)        |
|  `\(wyrażenie\)`  | wyrażenie grupujące, dopasowany łańcuch można później wykorzystać, np. wstawiając go w inne miejsce |              |                                        |
|                   | znak nowej linii                                                                                    |              |                                        |
|                   | znak tabulatora                                                                                     |              |                                        |
|   `[[:alnum:]]`   | znak alfanumeryczny (litery lub cyfry)                                                              |              |                                        |
|   `[[:alpha:]]`   | litera                                                                                              |              |                                        |
|   `[[:digit:]]`   | cyfra                                                                                               |              |                                        |
|   `[[:blank:]]`   | spacja lub tabulator                                                                                |              |                                        |
|   `[[:space:]]`   | znak odstępu (w tym nowej linii)                                                                    |              |                                        |

Zauważ, że znaczenie znaku `^` zmienia się w zależności od kontekstu - oznacza on początek linii lub negację zakresu znaków.

Wróćmy teraz do pliku `posortowane.txt` i zastanówmy się jak można opisać numery GenBank-u?

```
$: more posortowane.txt 

KU238865 Orobanche coerulescens
KY484464 Orobanche teucrii
KY484471 Orobanche kochii
KY484474 Orobanche elatior
KY484489 Orobanche mayeri
KY484493 Orobanche flava
KY484502 Phelipanche ramosa
KY484503 Phelipanche purpurea
```

Jest to ciąg znaków zaczynający się na początku linii, zawierający litery i cyfry (znali alfanumeryczne) po którym występuje spacja (jeden ze znaków odstępu).

Teraz poprzednio użyte wyrażenie powinno być bardziej zrozumiałe:

```
$: sed 's/^[[:alnum:]]\+[[:space:]]//' posortowane.txt
```

* `^` początek linii
* `[[:alnum:]]\+` jeden lub więcej znaków alfanumerycznych
* `[[:space:]]` znak odstępu.

> Zadanie: Spróbuj usunąć numery GenBank-u używając innego wyrażenia.

Po przejrzeniu powyższej tabeli powinno być jasne, dlaczego powyżej zamiast napisać `s/O./Orobanche/` napisałem `s/O\./Orobanche/`. Kropka oznacza jakikolwiek znak, ponieważ także oznacza kropkę oba wyrażenia zadziałają w tym przypadku (!) tak samo, ale jeśli chcemy być pewni, że dopasujemy znak `.`, to powinniśmy użyć przed nim znaku `\`, co spowoduje, że kropka zostanie odczytana dosłownie.

Zamieszczone w tabeli wyrażenie `\(wyrażenie\)` zapewne wydaje się niejasne. Pokażę zatem na przykładzie jak może być wykorzystane. Tym razem zadaniem będzie przeniesienie numeru GenBank-u na koniec linii. Można to zrobić tak:

```
$: sed 's/^\([[:alnum:]]\+[[:space:]]\)\(.*\)/\2 \1/' posortowane.txt

Orobanche coerulescens KU238865 
Orobanche teucrii KY484464 
Orobanche kochii KY484471 
Orobanche elatior KY484474 
Orobanche mayeri KY484489 
Orobanche flava KY484493 
Phelipanche ramosa KY484502 
Phelipanche purpurea KY484503
```

Powyżej wykorzystałem dwa wyrażenia grupujące. Pierwszy z nich obejmuje dopasowanie numeru GenBank-u (oraz spację), drugie pozostałą część tekstu (`.*` oznacza zero lub więcej dowolnych znaków. Po drugim ukośniku odwołuję się do wcześniej znalezionych dopasowań: `\1` oznacza pierwsze dopasowanie, `\2` drugie dopasowanie. Umieszczając je w kolejności `\2 \1` umieściłem pierwsze dopasowanie po drugim.

Zamianę można ograniczyć do linii, w których znajduje się inne dopasowanie. Na przykład:

```
$: sed "/^>/ s/A/0/g" plik.fasta
```

Zamieni znaki `A` na `0` w liniach, które zaczynają się od `>`.

Z kolei polecenie:

```
$: sed "/^>/! s/A/0/g" plik.fasta
```

Dokona zamiany w liniach, które **nie** rozpoczynają się od `>`.

Jeśli przed `s` w poleceniu umieścimy liczbę, będzie się ono odnosiło do linii w pliku o podanym numerze. Możemy np. w ten sposób dodać tekst w pierwszej linii w pliku `plik.txt`:

```
sed -i "1s/^/Tytuł dzieła\n/" plik.txt
```

Aby usunąć linie, w których znajduje się dany wzorzec stosujemy następującą komendę:

```
sed '/wzorzec/d' plik
```

Na przykład poniższa komenda usunie wszystkie linie z opisami sekwencji z pliku `sekwencje.fasta`

```
sed -i '/^>/d' sekwencje.fasta
```

### `tr` - zmiana i usuwanie znaków

Program `tr` ma podobne zastosowanie do `sed`-a, specjalizuje się jednak w zamianie pojedynczych znaków.

Używamy go w ten sposób:

```
tr opcje znaki1 znaki2
```

`znaki1` - to zestaw znaków które zmieniamy `znaki2` - to zestaw znaków na które zmieniamy

Pokażę to na kilku przykładach.

Zamiana `*` na `-`

```
$: echo "AGGC*TT" | tr "*" "-"

AGGC-TT
```

Usunięcie znaków `-`. Tak można łatwo usunąć z sekwencji znaki oznaczające indele.

```
$: echo "AG--GC-TT" | tr -d "-"

AGGCTT
```

Zamiana `A` na `T`, `C` na `G`, `G` na `C` oraz `T` na `A`.

```
$: echo "AGGCTT" | tr "ACGT" "TGCA" 

TCCGAA
```

W ten sposób można otrzymać łatwo sekwencję komplementarną. Zauważ, że powyższa komenda nie zmienia sekwencji `ACGT` na `TGCA`, ale podmienia poszczególne znaki.

`tr` nie ma możliwości bezpośredniej modyfikacji pliku, dlatego jeśli chcemy zmienić plik, użyjemy znaków przekierowania `<` do pliku wejściowego i `>` dla pliku wyjściowego.

Utwórz plik `sekwencje.txt` o zawartości:

```
CGACCAGATTACGGGGCCCATTA
CGAGACATTTATATACGATATAG
AAATTTCGCGCGCAGATAGCATA
```

Teraz utworzymy dla tych krótkich sekwencji sekwencje komplementarne:

```
tr "ACGT" "TGCA" < sekwencje.txt > sekwencje_komplementarne.txt
```

albo

```
cat sekwencje.txt | tr "ACGT" "TGCA" > sekwencje_komplementarne.txt
```

Otrzymujemy plik `sekwencje_komplementarne.txt`:

```
GCTGGTCTAATGCCCCGGGTAAT
GCTCTGTAAATATATGCTATATC
TTTAAAGCGCGCGTCTATCGTAT
```

Teraz połączmy te sekwencje w jedną, długą. W tym celu usuniemy  oznaczający znak nowej linii.

```
tr -d "\n" < sekwencje_komplementarne.txt > polaczona_sekwencja_komplementarna.txt
```

Tu też oczywiście można też użyć komendy `cat` i znaku `|`:

```
cat sekwencje_komplementarne.txt| tr -d "\n" > polaczona_sekwencja_komplementarna.txt
```

Plik wynikowy, zgodnie z założeniem, posiada jedną długą sekwencję:

```
GCTGGTCTAATGCCCCGGGTAATGCTCTGTAAATATATGCTATATCTTTAAAGCGCGCGTCTATCGTAT
```

### `rev` - odwracanie łańcucha znaków

Bardzo prostym, ale użytecznym narzędziem jest `rev` (od *reverse*), który odwraca łańcuch znaków.

```
$: echo "ACGTTT" | rev

TTTGCA
```

Teraz można sekwencję w pliku `polaczona_sekwencja_komplementarna.txt` także odwrócić. W efekcie otrzymamy sekwencję komplementarną, odwróconą.

```
cat polaczona_sekwencja_komplementarna.txt | rev > polaczona_sekwencja_komplementarna_odwrocona.txt
```

Cały proces łączenia przekształcania sekwencji w odwróconą i komplementarną można zmieścić w ,,jednolinijkowcu'' używając potoków i przekierowania:

```
cat sekwencja.txt | tr "ACGT" "TGCA" | tr -d "\n" | rev > sekwencja_RC.txt
```

### `uniq` - usuwanie powtórzeń

Polecenie `uniq` pozwala na usuwanie powtarzających się linii. Na przykład chcielibyśmy otrzymać listę rodzajów z taksonów znajdujących się w pliku `fasta` ale tak, aby każda nazwa występowała tylko raz.

Pobierz plik:

```
http://ggoralski.pl/files/filogenetyka-data/atp6-samples.fasta`
```

Najpierw uzyskajmy opisy sekwencji:

```
$: grep ">" atp6-samples.fasta 
>KY492922.1 Dendropicos elliotii isolate DA07 ATP6 (ATP6) gene, partial cds; mitochondrial
>KY492921.1 Dendropicos stierlingi isolate ZMUC74329 ATP6 (ATP6) gene, complete cds; mitochondrial
>KY492920.1 Dendropicos poecilolaemus isolate ZMUC72915 ATP6 (ATP6) gene, complete cds; mitochondrial
>KY492919.1 Campethera punctuligera isolate ZMUC56174 ATP6 (ATP6) gene, complete cds; mitochondrial
>KY492918.1 Campethera cailliautii isolate ZMUC56172 ATP6 (ATP6) gene, complete cds; mitochondrial
>KY492917.1 Dendropicos fuscescens isolate Z34 ATP6 (ATP6) gene, complete cds; mitochondrial
>KY492916.1 Campethera cailliautii isolate W45 ATP6 (ATP6) gene, complete cds; mitochondrial
>KY492915.1 Chloropicus xantholophus isolate MNHN2005922 ATP6 (ATP6) gene, complete cds; mitochondrial
....
```

Jak widać opisy są nieco bardziej złożone niż w poprzednio używanym pliku, ponadto jest ich więcej (powyżej widać tylko początek listy).

Teraz trzeba wyodrębnić nazwy rodzajowe. W naszym pliku jest to drugi ciąg znaków alfanumerycznych. Zastanów się jak go uzyskać?

```
$: grep ">" atp6-samples.fasta \
| sed 's/^>[[:alnum:]]\+\.[[:digit:]][[:space:]]\([[:alpha:]]\+\).\+/\1/'

Dendropicos
Dendropicos
Dendropicos
Campethera
Campethera
Dendropicos
Campethera
Chloropicus
Campethera
Dendropicos
...
```

Tym razem wykorzystałem jedno wyrażenie grupujące, odpowiadające nazwie rodzajowej, obejmujące ciąg znaków alfanumerycznych. Przed nim znajduje się numer GenBank-kowy sekwencji (znali alfanumeryczne, kropka, cyfra) ze spacją, a po nim pozostałe znaki w linii. Dopasowana całość (czyli cała linia) zostaje zamieniona na wyrażenie, które znalazło się w wyrażeniu grupującym (`\1`), czyli na nazwę rodzaju.

Szkopuł w tym, że nazwy te wyświetlają się wielokrotnie, a chcielibyśmy mieć listę rodzajów, taką w której każda występowałaby tylko raz. Dodajmy zatem polecenie `uniq`:

```
$: grep ">" atp6-samples.fasta | \
sed 's/^>[[:alnum:]]\+\.[[:digit:]][[:space:]]\([[:alpha:]]\+\).\+/\1/' \
| uniq

Dendropicos
Campethera
Dendropicos
Campethera
Chloropicus
Campethera
Dendropicos
Campethera
Ipophilus
...
```

Efekt jest nie do końca zgodny z oczekiwaniami. Co prawda duplikaty zostały usunięte, ale tylko wtedy gdy znajdowały się bezpośrednio po sobie. Tak właśnie działa `uniq`. Wykorzystajmy zatem polecenie, które sortuje linie, czyli `sort` wtedy wszystkie takie same linie znajdą się bezpośrednio przy sobie.

```
grep ">" atp6-samples.fasta | \
sed 's/^>[[:alnum:]]\+\.[[:digit:]][[:space:]]\([[:alpha:]]\+\).\+/\1/' \
| sort | uniq

Campethera
Chloropicus
Cooperia
Dendropicos
Ipophilus
Paracoccidioides
Pocillopora
```

> Zadanie: policz ile sekwencji i ile różnych nazw rodzajowych znajduje się w pliku
