SED i Awk
Przygotuj się do laboratorium
- Daniel Robbins - Sed by example: część 1, część 2, część 3 (pierwotnie było dostępne pod tym adresem)
- Daniel Robbins - Awk by example: część 1, część 2, część 3 (pierwotnie było dostępne pod tym adresem)
Wiedza
1. SED i Awk
- 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 – jeżeli nie wiesz dlaczego to jest zaleta, przypomnij sobie działanie narzędzi textutils z Podstawy skryptów i filtrów.
- 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!
SED:
- ang. Stream EDitor - 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? - zob. manual).
- Program wczytuje dane wejściowe linia po linii i dla każdej linii wykonuje wszystkie instrukcje!
AWK:
- Nazwa pochodzi od pierwszych liter nazwisk jego autorów: Alfreda V. Aho, Petera Weinbergera i Briana Kernighana
- 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.
2. 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. gitara (ale nie Gitara), zagadka, log.
- Wyrażenie ba opisuje wszystkie łańcuchy znaków zawierające podciąg ba np. baca, zabawa, żaba.
- 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. abakus, 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 w Lotto – w głównej grze Lotto losowane są liczby od 1 do 49. Przy pomocy wyrażenia regularnego można sprawdzić czy dana liczba może być wylosowana:
- ^[1-4]?[0-9]{1}$
- ^[1-4]{,1}[0-9]{1}$
- Adres MAC – 48-bitowy numer 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
- Jak krok po kroku zbudować wyrażenie regularne akceptujące powyższe zapisy?
- [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}$
Ćwiczenia
I. Wyrażenia regularne
Jeżeli chcesz pobawić się wyrażeniami regularnymi to warto odwiedzić: Regex Crossword
Jeżeli po prostu chcesz przetestować i zrozumieć wyrażenia regularne to w sieci dostępnych jest wiele webowych serwisów, które pozwalają na wpisanie wyrażenia, wyjaśniają jego poszczególne elementy i wyświetlają dopasowania na tekście, np.:
II. SED
Przeczytaj manual do programu. Zwróć uwagę na polecenia, ich składnię oraz sposób adresacji poleceń.
- Wyświetl plik /etc/passwd przy pomocy sed.
- Zamień separator - dwukropek - w pliku
/etc/passwd
na spację. - Wyświetl tylko loginy użytkowników zapisanych w pliku
/etc/passwd
- Wyświetl 4, 7, 10 i 13 linię pliku
/etc/passwd
- Wyświetl określone przedziałem (np. od 3. do 5. włącznie) linie pliku
/etc/passwd
. - Wyświetl linie pliku
/etc/passwd
opisujące osoby mające login zaczynający się na 'z'. - Wyświetl 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/passwd
)? - 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
. - 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ę odvideo
i wyświetl ich numer. - Napisz polecenie sed imitujące polecenie
cut -d: -f2
. - Napisz polecenie sed imitujące
cat -n
. - Napisać skrypt programu sed który ustawi powłokę startową na
/bin/tcsh
dla wszystkich użytkowników posiadającym folder domowy/nonexistent
(zmodyfikuje odpowiednio plik/etc/passwd
). Skrypt ma wydrukować całą zawartość zmienionego pliku na ekranie wraz z zaznaczeniem zmienionych linii - tak jak jest to przedstawione poniżej:---------------------------- linia w której nastąpiła zmiana powłoki ----------------------------
- Napisać skrypt programu sed wyświetlający linię zawarte w pliku
/etc/passwd
w odwrotnej kolejności.
III. AWK
- 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
orazEND
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.
- Zadanie1:
- Przeanalizować poniższy skrypt (plik userlist):
#!/bin/sh who | awk '{print $1}' | sort | uniq | xargs -i"{}" grep -e "^{}:" /etc/passwd | awk -f awkuserlist
oraz plik awkuserlist (skrypt programu awk) który jest w nim wykorzystany:
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>"; }
- Jaki jest rezultat wykonania skryptu userlist?
- Uwaga, na serwerze spk ten skrypt nie działa (dane użytkowników nie są przechowywane w /etc/passwd) - w takiej sytuacji zastąp fragment generujący aktualną listę obecności (
who | awk '{print $1}'
) przez fragment, który wprost zadaje przykładowe loginy z /etc/passwd (np.echo -e 'root\ngames\nman'
)
- 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:
<osoba> <dana1>wartosc1</dana1> <dana2>wartosc2</dana2> <dana3>wartosc3</dana3> <dana4>wartosc4</dana4> </osoba>
- Format wyjściowy:
------------------------------------------- dana1: wartosc1 dana2: wartosc2 dana3: wartosc3 dana4: wartosc4 -------------------------------------------
- Przykład:
- Dla wejścia:
<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>
- Skrypt powinien wygenerować wyjście:
------------------------------------------- imie: Jan nazwisko: Kowalski login: jkowalski uid: 1 gid: 1 home: /home/users/jkowalski shell: /bin/bash miasto: Kraków -------------------------------------------
- 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: publikacje.bib.
- Poszczególne wpisy bibliograficzne mają następującą postać:
@rodzaj{klucz, author = {wartość}, title = {wartość}, year = wartość, other = {wartość} }
- Zadanie polega na stworzeniu skryptu
bash
o nazwieszukaj
, który przy użyciuawk
wybierze z plikupublikacje.bib
tylko te wpisy bibliograficzne, które odpowiadają zapytaniu użytkownika. Skrypt powinien obsługiwać trzy opcje (-a
,-t
,-k
):./szukaj -a Nalepa
- wyszuka wszystkie publikacje autorstwa Nalepy (te wpisy bibtexowe, w których poluauthor
występujeNalepa
),./szukaj -t slowo
- wyszuka wszystkie publikacje zawierające w tytule ciągslowo
,./szukaj -k slowo
- wyszuka wszystkie publikacje zawierające ciągslowo
w dowolnym polu.
- 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:
login_1:ocena_1,ocena_2,ocena_3 login_2:ocena_1,ocena_2,ocena_3,ocena_4
- Założenia:
login_n
- jest loginem danego studenta- 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
Srednia ocena dla Login: X
gdzie
X
to wartość średniej oceny z dokładnością do dwóch miejsc po przecinku. - Skrypt powinien być w całości wykonywany przez awk - nie powinien używać żadnych poleceń zewnętrznych.
- Przykład:
- Dla wejścia:
spock:4.0, 4.5, 4.5, 5.0, 5.0, 2.0 scotty : 4.5, 3.0, 4.0, 3.5, 5.0, 2.0, 5.0, 5.0, 4.5 sulu:3.5 , 3.0, 5.0, 4.5, 2.0, 3.5, 4.5, 3.0, 2.0, 4.5, 4.0, 5.0, 2.0, 5.0, 3.5, 4.5, 5.0, 5.0, 4.5, 4.0, 5.0, 3.5, 5.0, 5.0, 5.0, 4.5, 5.0, 3.5, 3.5, 4.0, 4.0, 3.5, 4.0, 5.0, 3.0, 3.0 chekov:2.0, 4.5, 5.0 , 5.0, 4.0, 5.0, 5.0, 2.0, 3.0, 4.5 yar:3.0, 2.0, 5.0, 2.0, 3.0, 5.0, 3.0, 4.5, 5.0, 5.0, 5.0, 5.0, 4.5, 3.5, 2.0, 3.5, 4.0, 3.5, 2.0 enterprise:3.5, 2.0, 3.0, 3.0 riker : 3.0, 3.0, 3.0, 2.0, 4.0, 2.0, 5.0, 5.0, 4.0, 2.0, 5.0, 3.5, 3.0, 2.0, 2.0, 4.5, 4.5, 5.0, 3.5, 4.5, 3.5, 4.5, 3.0, 5.0, 5.0, 3.0, 3.5, 3.5, 5.0, 5.0, 4.5, 3.0, 2.0, 5.0, 5.0, 3.0, 4.5, 3.0 picard : 3.0 , 3.0, 3.0, 4.0, 3.5, 4.0, 3.0, 5.0, 5.0, 4.5, 4.0 laforge: 4.0, 4.5, 5.0, 3.5, 4.5, 4.5, 4.5, 2.0, 3.0, 3.0, 3.5 yoda:3.5, 4.0, 4.5, 4.5, 2.0 worf :2.0, 5.0, 4.0, 4.0, 3.5, 4.5 , 5.0 starfleet:5.0, 4.5, 3.5, 3.0, 4.0, 3.0, 4.0, 2.0, 3.0, 2.0, 5.0, 2.0, 3.5, 4.5, 4.5, 4.0, 3.0, 3.0, 3.5, 3.0, 3.0, 3.5, 2.0, 3.0, 3.5, 4.0, 4.5, 5.0, 3.5, 5.0, 3.5, 3.5, 3.5, 3.0, 5.0, 3.5, 4.5, 3.0, 4.5, 4.5, 5.0, 4.5, 3.5, 2.0, 3.0, 3.0, 4.0, 5.0, 5.0 kirk:4.0, 3.0, 5.0, 5.0, 5.0, 2.0, 2.0, 4.0 uhura : 3.5, 2.0, 5.0, 5.0, 5.0 , 4.0, 5.0, 5.0, 4.0, 4.5, 3.0, 3.0, 3.5, 4.0, 3.5, 4.0, 5.0, 4.0, 3.0, 4.5, 4.5, 5.0, 3.5, 4.0, 4.5, 5.0, 5.0, 5.0, 3.0, 5.0, 4.0, 4.0, 5.0, 4.5, 5.0, 3.0, 4.0, 5.0, 4.5, 2.0, 4.5, 5.0, 4.0, 3.0, 3.0, 4.5, 2.0, 2.0, 3.0, 3.5, 5.0
- Skrypt powinien wygenerować wyjście:
Srednia ocen dla spock: 4.17 Srednia ocen dla scotty: 4.06 Srednia ocen dla sulu: 4.01 Srednia ocen dla chekov: 4 Srednia ocen dla yar: 3.71 Srednia ocen dla enterprise: 2.88 Srednia ocen dla riker: 3.72 Srednia ocen dla picard: 3.82 Srednia ocen dla laforge: 3.82 Srednia ocen dla yoda: 3.70 Srednia ocen dla worf: 4 Srednia ocen dla starfleet: 3.68 Srednia ocen dla kirk: 3.75 Srednia ocen dla uhura: 4.03