|
|
pl:dydaktyka:unix:lab_sed_awk [2017/11/13 11:14] kkutt [SED] |
pl:dydaktyka:unix:lab_sed_awk [2019/06/27 15:50] |
====== SED i AWK ====== | |
| |
{{:pl:dydaktyka:unix:evolution_of_language.gif?400|}} | |
===== Do przygotowania ===== | |
* Przypomnieć sobie pracę z jednym z wybranych edytorów (np. vi, emacs, itp). | |
* Powtórzyć zasady pisania filtrów i skryptów powłoki sh. | |
* Przypomnieć sobie funkcję manipulujące łańcuchami znaków w języku C. | |
* Przeczytać następujące artykuły: | |
* Artykuł o //sed//: [[http://gentoo-handbook.lugons.org/doc/pl/articles/l-sed1.xml|część 1]], [[http://gentoo-handbook.lugons.org/doc/pl/articles/l-sed2.xml|część 2]], [[http://gentoo-handbook.lugons.org/doc/pl/articles/l-sed3.xml|część 3]] | |
* Artykuł o //awk//: [[http://gentoo-handbook.lugons.org/doc/pl/articles/l-awk1.xml|część 1]], [[http://gentoo-handbook.lugons.org/doc/pl/articles/l-awk2.xml|część 2]], [[http://gentoo-handbook.lugons.org/doc/pl/articles/l-awk3.xml|część 3]] | |
* Wersja angielska: | |
* Artykuł o //sed//: [[https://www.ibm.com/developerworks/linux/library/l-sed1/index.html|część 1]], [[https://www.ibm.com/developerworks/linux/library/l-sed2/index.html|część 2]], [[https://www.ibm.com/developerworks/linux/library/l-sed3/index.html|część 3]]. | |
* Artykuł o //awk//: [[https://www.ibm.com/developerworks/linux/library/l-awk1/index.html|część 1]], [[https://www.ibm.com/developerworks/linux/library/l-awk2/index.html|część 2]], [[https://www.ibm.com/developerworks/linux/library/l-awk3/index.html|część 3]]. | |
| |
===== Wprowadzenie ===== | |
* sed i awk są narzędziami strumieniowego przetwarzania tekstu. | |
* Ich główną zaletą jest możliwość czytania ze standardowego wejścia i wyświetlanie rezultatów na standardowym wyjściu (dlaczego jest to zaleta?). | |
* Dane mogą być wczytywane również z pliku. | |
* Podczas pracy nie modyfikują żadnych plików. | |
* Posiadają szereg instrukcji manipulujących łańcuchami znaków (wstawianie, zastępowanie, itp). | |
* Instrukcje mogą być podawane z linii komend (w przypadku małych ilości poleceń) lub czytane z pliku (co zwiększa czytelność w przypadku dużej ilości poleceń). | |
* Obsługują wyrażenia regularne!!! | |
| |
===== Wyrażenia regularne ===== | |
* ang. //regular expressions//, w skrócie //regex// lub //regexp// | |
* Są **wzorcami** opisującymi łańcuchy znaków. | |
* Umożliwiają odnalezienie określonej regularności w danej sekwencji znaków. | |
===== Definiowanie wyrażeń regularnych ===== | |
* Wyrażenia regularne składają się dwóch podstawowych zestawów znaków: | |
* Znaki kontrolne: ''. ? * + ^ | $ () [] {} \'' | |
* Zwykłe znaki: pozostałe | |
* Kolejność znaków w wyrażeniu jest **istotna**. | |
* Wielkość liter w wyrażeniu jest także **istotna**. | |
* Wyrażenie **g** opisuje wszystkie łańcuchy znaków zawierające literę **g** np. //**g**itara// (ale nie //Gitara//), //za**g**adka//, //lo**g**//. | |
* Wyrażenie **ba** opisuje wszystkie łańcuchy znaków zawierające podciąg **ba** np. //**ba**ca//, //za**ba**wa//, //ża**ba**//. | |
* Kropka **.** określa dowolny znak oprócz znaku końca wiersza np. **a.c** pasuje do: //aac//, //abc//, ale nie pasuje do //ac//. | |
* Nawias kwadratowy pozwala na określenie listy, zakresu znaków np. a[abc]c pasuje do napisów zawierających podciągi: //aac//, //abc//, //acc// - czyli pomiędzy znakami **a** i **c** może wystąpić tylko i wyłącznie jeden znak z podanych w nawiasie. **Uwaga**, należy zauważyć że powyższe wyrażenie pasuje również do: //aabc//, //acccc//, itp. Powyższe wyrażenie jest równoznaczne z **a[a-c]c** poprzez zdefiniowane zakresu za pomocą **-** myślnika. Istnieje także możliwość listowania zakresów: **[a-z0-9]** - oznacza dowolną małą literę od **a** do **z** lub dowolną cyfrę. | |
* Negacja zakresu ^. Znak ^ umieszczony jako pierwszy wewnątrz nawiasu kwadratowego oznacza jego negację np. **[^a-c]** oznacza wszystkie znaki oprócz **a**, **b**, **c**. | |
* Znak ^ ma także drugie znaczenie (żeby nie było tak prosto ^_^). Oznacza początek łańcucha znaków, czyli **^ab** określa wszystkie łańcuchy znaków rozpoczynające się od **ab** np. //**ab**akus//, natomiast nie pasuje do np: //żaba//. | |
* Podobne znaczenie do ^ ma znak **$** oznaczający koniec łańcucha znaków. | |
* Okrągłe nawiasy () służą do grupowania części wyrażeń (wykorzystywane do np. określania liczby powtórzeń danego wzorca). | |
* Powtórzenia: liczba powtórzeń określa ilość wystąpienia danego wzorca w badanym łańcuchu znaków. Liczbę powtórzeń możemy zdefiniować w następujący sposób: | |
* Gwiazdka * - zero lub więcej wystąpień poprzedzającego ją znaku np: **a*** pasuje do //ab//, //aab//, //bab//, //baaab//, ale też do: //b//. | |
* Plus + - co najmniej jedno wystąpienie poprzedzającego znaku np: **a+** pasuje do //ab//, //aab//, //bab//, //baaab//, ale **nie** do: //b//. | |
* Znak zapytania ? po symbolu oznacza najwyżej jedno (być może zero) wystąpienie poprzedzającego wyrażenia. | |
* Nawias klamrowy {} pozwala na dokładne określenie minimalnej i maksymalnej liczby powtórzeń: | |
* {2,5} - minimalnie 2 a maksymalnie 5, | |
* {3} - dokładnie 3 razy, | |
* {5,} - co najmniej 5 razy, | |
* {,6} - co najwyżej 6 razy. | |
* Pionowa kreska (ang. pipeline) | to operator OR np. jeśli napiszemy a|b|c oznacza to, że w danym wyrażeniu może wystąpić a lub b lub c. | |
* Jeżeli chcemy użyć jednego ze znaków kontrolnych jako zwykłego to poprzedzamy go backslash-em **\** np: **^\$** definiuje wszystkie łańcuchy znaków rozpoczynające się od znaku dolara ''$''. | |
| |
| |
| |
==== Przykłady ==== | |
=== Kod pocztowy === | |
Polski kod pocztowy składa się z pięciu cyfr i myślnika wstawionego po drugiej cyfrze. Przy pomocy wyrażeń regularnych wzorzec kodu pocztowego może być zdefiniowany następująco: | |
* **^[0-9][0-9]-[0-9][0-9][0-9]$** | |
* **^[0-9]{2}-[0-9]{3}$** | |
| |
=== Liczby dużego lotka === | |
W dużym lotku losowane są liczby od 1 do 49. Przy pomocy wyrażenia regularnego można sprawdzić czy dana liczba może być wylosowana w dużym lotku. Należy wyrażenia zdefiniować następująco: | |
* **^[1-4]?[0-9]{1}$** | |
* **^[1-4]{,1}[0-9]{1}$** | |
| |
=== Adres MAC === | |
Adres MAC jest 48-bitowym numerem karty sieciowej. Najczęściej zapisuje się go w postaci heksadecymalnej. Po każdych dwóch znakach może występować (ale nie musi) separator (najczęściej są to: dwukropek, myślnik, spacja). Przykładowy format adresu MAC to: | |
* 1F:34:C6:78:90:AB | |
* 1F-34-C6-78-90-ab | |
* 1f 34 c6 78 90 AB | |
* 1F34C67890AB | |
Wyrażenie regularne akceptujące powyższe zapisy może być zdefiniowane następująco: | |
- **[A-Fa-f0-9]{2}** - pierwsze dwa znaki (8 bitów bez separatora), | |
- **[-: ]?** - separatory występujące co najwyżej jeden raz, | |
- **[A-Fa-f0-9]{2}** - kolejne części adresu (ostatnie 40 bitów) | |
- Wyrażenie składające się z wyr. 2 i 3 trzeba powtórzyć 5 razy: **([-: ]?[A-Fa-f0-9]{2}){5}** | |
- Czyli całość może wyglądać następująco: **^[A-Fa-f0-9]{2}([-: ]?[A-Fa-f0-9]{2}){5}$** | |
| |
===== SED ===== | |
* ang. **S**tream **ED**itor - edytor strumieniowy. | |
* Pomimo swej prostoty umożliwia wykonanie złożonych operacji na łańcuchach znaków. | |
* Program przyjmuje polecenia do wykonania z pliku lub z wiersza poleceń (jak zdefinować instrukcje do wykonania z linii poleceń a jak z pliku? - manual). | |
* Program wczytuje dane wejściowe linia po linii i **dla każdej linii wykonuje wszystkie** instrukcje do wykonania!!! | |
| |
===== AWK ===== | |
* Nazwa pochodzi od pierwszych liter nazwisk jego autorów: //Alfreda V. **A**ho, Petera **W**einbergera i Briana **K**ernighana// | |
* Jest to program interpretujący język skryptowy, służący do przetwarzania wyrażeń regularnych. | |
* Składnia języka jest bardzo podobna do składni języka C. | |
* Program wczytuje dane wejściowe rekord po rekordzie i dla każdego rekordu wykonuje wszystkie polecenia. | |
| |
| |
===== Ćwiczenia ===== | |
==== SED ==== | |
| |
Przeczytać manual do programu. Zwrócić uwagę na polecenia, ich składnię oraz sposób adresacji poleceń. | |
| |
- Wyświetlić plik **/etc/passwd** przy pomocy //sed//. | |
- Zamienić separator - dwukropek - w pliku ''/etc/passwd'' na spację. | |
- Wyświetlić **tylko** loginy użytkowników zapisanych w pliku ''/etc/passwd'' | |
- Wyświetlić 4, 7, 10 i 13 linię pliku ''/etc/passwd'' | |
- Wyświetlić określone przedziałem (np. od 3. do 5. włącznie) linie pliku ''/etc/passwd''. | |
- Wyświetlić linie pliku ''/etc/passwd'' opisujące osoby mające login zaczynający się na 'z'. | |
- Wyświetlić linie pliku ''/etc/passwd'' opisujące osoby mające login zaczynający się na 'w' lub 'z'. | |
- Jak przy pomocy sed zaimitować polecenie ''grep -v''? np. dla frazy 'lo' (''grep -v lo /etc/networks'') | |
- Jak zamienić w pliku wszystkie słowa ''root'' na twój login (przetestuj na pliku ''/etc/aliases'')? | |
- Jak zamienić słowo 'root' na twój login w pliku, ale tylko w wierszach, w których występuje 'www'? A jak tam gdzie nie występuje? | |
- Jak usunąć z pliku puste linie? | |
- Jak zamienić przy pomocy sed wszystkie litery 'r' na 'k'? | |
- W jaki sposób zakodować szyfrem ROT13 plik przy pomocy sed (szyfr zamienia litery na występujące 13 liter dalej, np. a<->n, b<->o, itd.)? | |
- Przy pomocy polecenia sed zakomentuj linijkę link-local w pliku ''/etc/networks''. | |
- 8-o W jaki sposób przy użyciu sed wstawić kolumnę X po pierwszym znaku wiersza (dodatkowy znak X w każdym wierszu)? A jak po piątym? | |
- Jak przy pomocy sed powtórzyć 3 razy pierwsze dwie litery każdego wiersza w pliku? | |
- Wylistuj wszystkie wiersze pliku ''/etc/mime.types'' zaczynające się od ''video'' i wyświetl ich numer. | |
- Napisz polecenie sed imitujące polecenie ''cut -d: -f2''. | |
- W jaki sposób zmienić kolejność słów (np. w pliku ''/etc/aliases'')? \\ Jest: ''news: root'' \\ Zrób: ''root: news'' | |
- 8-o Napisz polecenie sed imitujące ''cat -n''. | |
- 8-o Napisać **skrypt** programu //sed// który ustawi powłokę startową wszystkim użytkownikom grupy //is1// (folder domowy w katalogu //is1//) na ''/bin/tcsh''. Skrypt ma wydrukować całą zawartość zmienionego pliku na ekranie wraz z zaznaczeniem zmienionych linii - tak jak jest to przedstawione poniżej.<code> | |
---------------------------- | |
linia w której nastąpiła zmiana powłoki | |
---------------------------- | |
</code> | |
- 8-o Napisać **skrypt** programu //sed// wyświetlający linię zawarte w pliku ''/etc/passwd'' w odwrotnej kolejności. | |
| |
==== AWK ==== | |
=== Ćwiczenia === | |
- Przeczytać manual do programu zwrócić uwagę na: | |
* wbudowane zmienne, | |
* wbudowane funkcje, | |
* wbudowane instrukcje sterujące. | |
- Jak //awk// interpretuje spacje? | |
- Co określają następujące zmienne: ''FS'', ''RS'', ''NF'', ''NR'', ''OFS'', ''ORS''? | |
- Kiedy wykonywane są instrukcje zawarte w blokach ''BEGIN'' oraz ''END'' w przypadku kiedy zródło danych składa się z wielu plików? | |
- Zapisać dowolną stronę internetową (może być także ta) w formacie HTML. Wyświetlić jej zawartość przy pomocy polecenia //cat// a następnie przy pomocy programu //awk// usunąć wszystkie znaczniki HTML z treści. Rezultat należy wyświetlić na ekranie. | |
- Do powyższego zadania dopisać element filtru, który usunie wszystkie puste linie, oraz te które posiadają tylko białe znaki. | |
| |
=== Zadania === | |
\\ | |
**8-) 8-) 8-) Zadanie1** \\ | |
\\ | |
Przeanalizować poniższy skrypt (plik **userlist**): | |
<code> | |
#!/bin/sh | |
| |
who | awk '{print $1}' | sort | uniq | xargs -i"{}" grep -e "^{}:" /etc/passwd | awk -f awkuserlist | |
</code> | |
oraz plik **awkuserlist** (skrypt programu //awk//) który jest w nim wykorzystany: | |
<code> | |
BEGIN { | |
FS=":" | |
print "<xml version="1.0">"; | |
} | |
{ | |
match($5, "^[^, ]*"); | |
imie=substr($5, RSTART, RLENGTH); | |
match($5, " [^, ]*"); | |
nazwisko=substr($5, RSTART+1, RLENGTH-1); | |
login=$1; | |
uid=$3; | |
gid=$4; | |
home=$6; | |
shell=$7; | |
print "<osoba>"; | |
print "<imie>"imie"</imie>"; | |
print "<nazwisko>"nazwisko"</nazwisko>"; | |
print "<login>"login"</login>"; | |
print "<uid>"uid"</uid>"; | |
print "<gid>"gid"</gid>"; | |
print "<home>"home"</home>"; | |
print "<shell>"shell"</shell>"; | |
print "</osoba>"; | |
} | |
END { | |
print "</xml>"; | |
} | |
</code> | |
* Jaki jest rezultat wykonania skryptu **userlist** | |
* Dopisać skrypt programu //awk// który przeanalizuje strumień wyjściowy skryptu **userlist** i przekształci go w następujący sposób: | |
* Format wejściowy: | |
<code xml> | |
<osoba> | |
<dana1>wartosc1</dana1> | |
<dana2>wartosc2</dana2> | |
<dana3>wartosc3</dana3> | |
<dana4>wartosc4</dana4> | |
</osoba> | |
</code> | |
* Format wyjściowy: | |
<code> | |
------------------------------------------- | |
dana1: wartosc1 | |
dana2: wartosc2 | |
dana3: wartosc3 | |
dana4: wartosc4 | |
------------------------------------------- | |
</code> | |
* Przykład: | |
* Dla wejścia: | |
<code xml> | |
<osoba> | |
<imie>Jan</imie> | |
<nazwisko>Kowalski</nazwisko> | |
<login>jkowalski</login> | |
<uid>1</uid> | |
<gid>1</gid> | |
<home>/home/users/jkowalski</home> | |
<shell>/bin/bash</shell> | |
<miasto>Kraków</miasto> | |
</osoba> | |
</code> | |
* Skrypt powinien wygenerować wyjście: | |
<code> | |
------------------------------------------- | |
imie: Jan | |
nazwisko: Kowalski | |
login: jkowalski | |
uid: 1 | |
gid: 1 | |
home: /home/users/jkowalski | |
shell: /bin/bash | |
miasto: Kraków | |
------------------------------------------- | |
</code> | |
| |
\\ | |
** 8-) 8-) 8-) Zadanie 2** \\ | |
\\ | |
**BibTeX** to narzędzie służące do formatowania bibliografii. | |
Operuje ono na danych zawartych w plikach o rozszerzeniu "bib" | |
zawierających dane bibliograficzne. | |
Przykład pliku: {{:pl:dydaktyka:unix:publikacje.bib.txt|}}. | |
| |
Poszczególne wpisy bibliograficzne mają następującą postać: | |
<code> | |
@rodzaj{klucz, | |
author = {wartość}, | |
title = {wartość}, | |
year = wartość, | |
other = {wartość} | |
} | |
</code> | |
Zadanie polega na stworzeniu skryptu ''bash'', | |
który przy użyciu ''awk'' wybierze z pliku tylko te wpisy bibliograficzne, | |
które odpowiadają zapytaniu użytkownika. | |
Tzn. skrypt zapisany w pliku ''szukaj'' powinien obsługiwać | |
następujące opcje (-a, -t, -k): | |
* ''./szukaj -a Nalepa'' - wyszuka wszystkie publikacje autorstwa Nalepy (te wpisy bibtexowe, w których polu ''author'' występuje ''Nalepa''), | |
* ''./szukaj -t slowo'' - wyszuka wszystkie publikacje zawierające w tytule ciąg ''slowo'', | |
* ''./szukaj -k slowo'' - wyszuka wszystkie publikacje zawierające ciąg ''slowo'' w dowolnym polu. | |
| |
\\ | |
** 8-) 8-) 8-) Zadanie 3** \\ | |
\\ | |
Napisz skrypt programu awk który policzy i wyświetli średnią ocen dla każdego studenta. | |
Lista studentów oraz ocen jest zapisana w pliku, którego nazwę podajemy jako parametr uruchomienia programu awk (nazwa pliku nie może być zakodowana wewnątrz skryptu). | |
Format pliku zawierającego listę studentów oraz ocen jest następujący:<code> | |
login_1:ocena_1,ocena_2,ocena_3 | |
login_2:ocena_1,ocena_2,ocena_3,ocena_4</code> | |
Należy przyjąć następujące założenia: | |
- ''login_n'' - jest loginem danego studenta. | |
- ''login_n'' - istnieje w pliku ''/etc/passwd''. | |
- Liczba studentów nie jest określona, definiuje ją tylko i wyłącznie liczba linii w pliku. | |
- Plik zawiera tylko linie w powyższym formacie - przyjmujemy jako aksjomat i nie weryfikujemy tej kwestii. | |
- ''ocena_n'' - oznacza ocenę, liczbę ze zbioru ''{2.0, 3.0, 3.5, 4.0, 4.5, 5.0}'' (nie ma przymusu sprawdzania poprawności - przyjmujemy że oceny są wpisane poprawnie). | |
- Liczba ocen dla każdego studenta nie jest określona i może być różna. | |
- Spacje w pliku nie wpływają na sposób jego przetwarzania. | |
Jako rezultat, skrypt powinien wyświetlić informację o uzyskanej średniej ocenie przez każdego studenta w następującym formacie:<code>Srednia ocena dla Imie Nazwisko wynosi: X</code> | |
gdzie: | |
- ''Imie Nazwisko'' - oznaczają odpowiednio imię i nazwisko studenta pobrane z pliku ''/etc/passwd'' (uwaga: nie login jak to jest w pliku wejściowym). | |
- ''X'' - wartość średniej oceny z dokładnością do dwóch miejsc po przecinku. | |
Napisany skrypt nie może używać żadnych poleceń zewnętrznych.\\ | |
Przykłady plików: {{:pl:dydaktyka:unix:input.txt|wejściowego}} oraz {{:pl:dydaktyka:unix:output.txt|wyjściowego}}. | |
| |
===== Dla zainteresowanych ===== | |
| |
* Artykuł o //sed//: [[http://www.ibm.com/developerworks/linux/library/l-sed1.html|część 1]], [[http://www.ibm.com/developerworks/linux/library/l-sed2.html|część 2]], [[http://www.ibm.com/developerworks/linux/library/l-sed3.html|część 3]]. | |
* Artykuł o //awk//: [[http://www.ibm.com/developerworks/linux/library/l-awk1.html|część 1]], [[http://www.ibm.com/developerworks/linux/library/l-awk2.html|część 2]], [[http://www.ibm.com/developerworks/linux/library/l-awk3.html|część 3]]. | |