pdf - Środowisko programisty
Transkrypt
pdf - Środowisko programisty
Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 Wyrażenia regularne, grep i wprowadzenie do sed-a Wyrażenie regularne to sekwencja znaków opisująca wzorzec tekstowy, czyli zbiór słów odpowiadających podanemu opisowi. Program grep służy do wyszukiwania wzorców tekstu w tekście. Typowe zastosowanie to wyszukiwanie małego słowa/wzorca w wielkim pliku tekstowym. Wzorce opisywane są przez wyrażenia regularne, których grep obsługuje trzy rodzaje. Wywołany z flagą: -G interpretuje wzorzec jako basic regular expression (BRE), wersja domyślna; -E interpretuje wzorzec jako extended regular expression (ERE); -P interpretuje wzorzec jako perlowe wyrażenie regularne. Grep jest w systemie Unix prawie od początku, pierwsza wersja powstała w 1973 roku. Kilka kolejnych opcji wywołania programu grep omawiamy na końcu zestawu. Zaczynamy od typowych wywołań, np.: grep ’krowa’ tekst.in (drukuje wszystkie linie w pliku tekst.in zawierające tekst krowa) W powyższym przykładzie krowa jest wyrażeniem regularnym. Na wyrażenie regularne składają się znaki trzech różnych typów: znaki pozycjonujące, zbiory znaków oraz znaki modyfikujące. Znaki pozycjonujące to: ^ $ \< \> początek linii koniec linii początek słowa koniec słowa Powyższe znaki pełnią funkcję pozycjonujących tylko wtedy, gdy są w odpowiednim miejscu wyrażenia regularnego. Przykłady: ^A A$ A^ $A ^^ $$ „A” na początku linii „A” na końcu linii „A^” gdziekolwiek w linii „$A” gdziekolwiek w linii „^” na początku linii „$” na końcu linii Najprostszym wyrażeniem regularnym są ciągi liter. Dla przykładu wyrażenie „a” odpowiada po prostu literze a, natomiast wyrażenie „krowa” odpowiada słowu krowa. Strona 1/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 Zbiory znaków (z przykładami użycia): . ^.$ ^[0123456789]$ ^[0-9]$ [A-Za-z0-9_] ^T[a-z][aeiou] [] [0] [^0-9] [-0-9] [0-9-] [^-0-9] []0-9] [0-9]] [0-9-z] [0-9\-a\]] linia, w której występuje dowolny znak linia zawierająca dokładnie jeden znak linia zawierająca dokładnie jedną cyfrę to samo, co wyżej, ale krócej zapisane linia z co najmniej jedną literą, cyfrą lub podkreślnikiem linia zaczynająca się od T, potem dowolnej litery i samogłoski łacińskiej linia zawierająca nawiasy kwadratowe linia zawierająca 0 linia zawierająca znak, który nie jest cyfrą linia zawierająca znak, który jest cyfrą lub minusem to samo, co wyżej linia zawierająca znak, nie który jest cyfrą ani minusem linia zawierająca znak, który jest cyfrą albo ] linia zawierająca cyfrę, a po niej ] linia zawierająca cyfrę lub znak z zakresu od 9 do z linia zawierająca cyfrę, minus, „a” lub ] Istnieją również alternatywne oznaczenia dla zbiorów - klasy znaków POSIX: [:alnum:] [:alpha:] [:blank:] [:cntrl:] [:digit:] [:graph:] [:lower:] [:print:] [:space:] [:upper:] [:xdigit:] A-Za-z0-9 A-Za-z spacja lub tabulator znaki kontrolne 0-9 znaki graficzne, czyli znaki o kodach ACSII 33-126 a-z znaki drukowalne (znaki graficzne + spacja) białe znaki A-Z 0-9A-Fa-f Uwaga! Te klasy wymagają ujęcia w dodatkową parę nawiasów kwadratowych, np.: [[:digit:]] zbiór oznaczający dowolną cyfrę [[:alnum:]_] zbiór oznaczający dowolną literę, cyfrę bądź podkreślnik Znaki modyfikujące: zmienia znaczenie następnego znaku ze specjalnego na zwykły następując po znaku (lub wyrażeniu w nawiasach) sprawia, że * 0 lub więcej razy następując po znaku (lub wyrażeniu w nawiasach) sprawia, że \+ 1 lub więcej razy następując po znaku (lub wyrażeniu w nawiasach) sprawia, że \? 0 lub 1 raz \ i na owdrót ten występuje ten występuje ten występuje Strona 2/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 Co więcej, można nakazać danej sekwencji znaków pojawiać się dokładnie określoną liczbę razy przy pomocy wstawienia przedziału wewnątrz \{ i \}. Przykłady: dowolna linia z gwiazdką to samo, co wyżej dowolna linia z backslashem dowolna linia zaczynająca się dowolna linia dowolna linia zaczynająca się dowolna linia zaczynająca się dowolna linia zaczynająca się ^A*B poprzedzającym B ^AA*B dowolna linia zaczynająca się ^A\+B to samo, co wyżej ^A\?B dowolna linia zaczynająca się ^A\{4,8\}B dowolna linia zaczynająca się ^A\{4,\}B dowolna linia zaczynająca się ^A\{4\}B dowolna linia zaczynająca się ^\{4,8\} dowolna linia zaczynająca się A{4,8} dowolna linia z A4,8 * \* \\ ^* ^A* ^A\* ^AA* od gwiazdki od A* od A od B lub jednym lub więcej A, jednym lub więcej A, poprzedzającym B od od od od od A lub od AB 4, 5, 6, 7 lub 8 A, poprzedzających B co najmniej 4 A, poprzedzających B AAAAB 4,8 Istnieje również wzorzec pozwalający wyszukiwać słowa z powtórkami. Przykładowo, wyrażenie [a-z][a-z] wskaże nam dwie sąsiednie małe litery, ale niekoniecznie takie same (podobnie jak [a-z]* wskaże nam dowolnej długości ciąg małych liter, lecz niekoniecznie różnych). Wyjściem jest zastosowanie nawiasów: \(\) i \), w które zamykamy interesujący nas podwzorzec, by później odwołać się do niego przy pomocy cyfry poprzedzonej backslachem. Dysponujemy 9 takimi slotami pamięci, oznaczonymi od \1 od \9. Przykładowo, pięcioliterowych palindromów (takich jak np. „radar”) możemy szukać przez następujące wyrażenie regularne: \([a-z]\)\([a-z]\)[a-z]\2\1 Wyrażenia regularne wykorzystywane w grepie pozwalają na używanie alternatywy słów, a nie jedynie alternatywy znaków. W przypadku wyszukiwania linii ze ciągami znaków „alfa” lub „beta” możemy postąpić tak, jak poniżej: grep ’\(alfa\|beta\)’ tekst.in W pewnych sytuacjach jednak chcialibyśmy wyszukiwać słowa w potocznym rozumieniu tego terminu. \<[dD]om\> pozwala na wyszukanie ciągów znaków dom lub Dom, ale tylko takich, które nie są poprzedzone Strona 3/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 literą, liczbą lub podkreślnikiem. Po literze m również nie może wystąpić litera, liczba lub podkreślnik. Podamy teraz kilka przydatnych opcji polecenia grep: grep -c ’^[A-Z]’ tekst.in grep -i ’ErRoR’ tekst.in grep -n ’^[A-Z]’ tekst.in grep -v ’^[A-Z]’ tekst.in grep -f wzorce.in tekst.in grep -F lista tekst.in wypisuje liczbę linii zawierających podany wzorzec wypisuje linie zawierające słowo „error”, „ERROR”, „eRrOr” itd. (parametr -i sprawia, że grep nie rozróżnia pomiędzy małymi a dużymi literami) wypisuje linie zawierające podany wzorzec wraz z numerami tych linii w pliku wypisuje linie, w których nie występuje wzorzec (w tym przypadku linie, które nie zaczynają się od dużej litery) wypisuje linie zawierające wzorce z pliku „wzorzec.in” (jeden wzorzec musi zawierać się w jednej linii) dzieli wzorzec „lista” na ciąg wozrców (muszą być oddzielone znakiem nowej linii) i wypisuje linie „tekst.in” zawierające te wzorce Najpopularniejszym zastosowaniem polecenia grep, oprócz wyszukiwania wzorca w pojedynczym pliku tekstowym, jest przeszukiwanie zawartości wszystkich plików w podanym katalogu (rekurencyjnie): grep -r "wzorzec" Projekt wypisze informacje o wszystkich wystąpieniach „wzorca” w katalogu „Projekt” i jego podkatalogach. Grep w edytorze Vim W naszym ulubiony edytorze vim możemy używać polecenia :grep w trybie normalnym. Poza tym jest wbudowana komenda: :vim[grep][!] /{pattern}/[g][j] {file} ..., która szuka wzorca {pattern} w plikach {file}.... Użycie flagi g pozwala na zwrócenie więcej niż jednego dopasowania w linii. Jeśli nie użyjemy flagi j skaczemy do pierwszego wystąpienia wzorca pattern. Możemy znaleźć tylko pierwsze k wystąpień jeśli poprzedzimy komendę liczbą k. W następującym przykładzie znajdujemy tylko pierwsze wystąpienie. :1vim[grep][!] /{pattern}/[g][j] {file} ... Jeśli użyjemy [!], to wszystkie zmianny w aktualnie modyfikowanym buforze (pliku) zostaną porzucone. Strona 4/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 sed sed to edytor strumieniowy (stream editor ) zawarty w systemach uniksowych, służący do przetwarzania plików tekstowych. Jego funkcjonalność można docenić już po kilku pierwszych minutach użytkowania. Podstawowa komenda wykorzystywana w większości zastosowań to s (od substitution). sed ’s/day/night/’ < oldfile > newfile echo "Sunday" | sed ’s/day/night/’ −→Sunnight Można wyróżnić cztery składniki powyższego użycia komendy s: nazwa komendy (s), separatory (/) , regularne wyrażenie dla wzorca (day), ciąg zastępujący (night). Separator / nie zawsze jest najwygodniejszy, dlatego można wykorzystać też inne znaki: sed sed sed sed ’s/\/usr\/local\/bin/\/common\/bin/’ ’s_/usr/local/bin_/common/bin_’ ’s:/usr/local/bin:/common/bin:’ ’s /usr/local/bin /common/bin ’ (tak, spacja też działa) Sed przetwarza wejście linia po linii. W każdym kolejnej iteracji takiej pętli następna linia jest umieszczana w komórce pamięci zwanej pattern space i wykonywane są na niej po kolei wszystkie wyszczególnione komendy. Niektóre komendy takie jak np. n, albo d powodują, żę sed przechodzi do kolejnej iteracji. Komenda q wymusza zamknięcie sed. Do omówienia tych i innych komend przejdziemy za chwilę. Na razie zatrzymamy się przy komendzie s. Domyślnie (bez dodatkowych opcji) zamienia ona tylko pierwsze wystąpienie wzorca na ciąg zastępujący. Jeśli w pliku tekst.in znajduje się: one two three, one two three four three two one one hundred To instrukcja sed ’s/one/ONE/’ < tekst.in wydrukuje ONE two three, one two three four three two ONE ONE hundred Użycie znaku & w ciągu zastępującym odpowiada ciągowi dopasowanemu do wzorca: echo "abc 123" | sed ’s/[0-9]*/& &/’ −→" abc 123" Strona 5/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 echo "abc 123" | sed ’s/[0-9][0-9]*/& &/’ −→"abc 123 123" echo "abc 123" | sed ’s/[0-9]\+/& &/’ −→"abc 123 123" Należy zwrócić uwagę, że wyrażenie regularne w powyższych poleceniach jest dopasowywane do najwcześniejszego miejsca w tekście, a spośród dopasowań zaczynających się w tym samym miejscu wybierany jest najdłuższy tekst pasujący do wzorca. Dlatego w pierwszym przypadku [0-9]* dopasowuje się do znaku pustego. Czy sed może się zapętlić zastępując wzorzec dwoma wystąpieniami wzorca, później czteroma, itd.? Nie. sed wyszukuje wzorców w liniach tekstu na wejściu. Gdy już dokona podstawienia zmodyfikowana linia drukowana jest na wyjściu i nie jest dalej przetwarzana. Zatem echo "a" | sed ’s/a/aa/’ wydrukuje dwie literki a. Użycie \1 we wzorcu lub w ciągu zastępującym odpowiada ciągowi dopasowanemu w bloku wzorca wyznaczonym przez \(...\) (lub (...) przy opcji -r): echo "rower gruszka" |sed -r ’s/([a-z]+) ([a-z]+)/\2 \1/’ −→"gruszka rower" echo "abc 123 123 def" | sed ’s/\([123]*\) \1/\1/’ −→"abc123 123 def" echo "abc 123 123 def" | sed ’s/\([123]\+\) \1/\1/’ −→"abc 123 def" Flaga g powoduje zastąpienie wszystkich wystąpień wzorca w linii ciągiem zastępującym: echo "abc −→"(abc) echo "abc −→"(abc) 123" | sed -r ’s/[^ ]+/(&)/’ 123" 123" | sed -r ’s/[^ ]+/(&)/g’ (123)" Flaga liczbowa k powoduje zastąpienie k-tego dopasowania wzorca ciągiem zastępującym. echo "abcdef" | sed ’s/./&:/4’ −→"abcd:ef" Domyślnie sed drukuje każdą linię. Jeśli wykonywane jest zastąpienie, nowa linia jest drukowana zamiast starej. Gdy użyjemy opcji -n domyślne drukowanie będzie wyłączone. Flaga p — drukowanie linii zawierających dopasowanie do wzorca. sed -n ’s/[żź]/Z/gp’ Flaga w — zapisanie do podanego pliku linii zawierających dopasowanie do wzorca. sed -n ’s/^[0-9]*[02468]$/&/w even’ < file Strona 6/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 Flaga I — ignoruje wielkość liter przy dopasowywaniu. echo "ABC" | sed ’s/aBc/#/I’ Flagi można łączyć: sed -n ’s/a/A/2pw /tmp/file’ < old > new Tak jak wspominaliśmy w kązdej iteracji sed może wykonywać na ciągu przechowywanym w pattern space więcej niż jedną komendę. Komendy można łączyć używając flagi -e tak jak poniżej: sed ’s/BEGIN/begin/’ < old | sed ’s/END/end/’ > new sed -e ’s/BEGIN/begin/’ -e ’s/END/end/’ < old > new Ciąg komend seda można również zapisać jako skrypt (kolejne komendy możemy oddzielać od siebie za pomocą znaków nowej linii lub średników): #!/bin/bash sed ’ s/a/A/g s/e/E/g s/i/I/g s/o/O/g s/u/U/g’ Każdą kolejną komendę można zawęzić do pojedynczej linii, przedziału linii, czy też linii pasujących do podanego wzorca. W pierwszym przykładzie ograniczamy się do linii nr 3 i tylko w niej zamieniamy niepusty ciąg cyfr przez znak pusty. sed ’3 s/[0-9][0-9]*//’ < old > new W drugim przykładzie ograniczamy się do linii zaczynających się od # sed ’/^#/ s/[0-9][0-9]*//’ Możemy ograniczyć się do przedziału linii. W następnym przykładzie używamy komendy s do pierwszych stu linii. W kolejnym do lini od 101 do ostatniej $. sed ’1,100 s/A/a/’ sed ’101,$ s/A/a/’ Oznaczenia końców przedziałów można mieszać. Możemy np. rozpatrywać linie od pierwszej do pierwszej takiej, która zawiera słowo: start. sed ’1,/start/ s/#.*//’ Strona 7/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 W kolejnym przykładzie, poniżej, zawężenie /start/,/stop/ działa w następujący sposób: (1) sed szuka pierwszej linii zawierającej słowo ciąg liter start; (2) począwszy od tej linii (włącznie) komenda jest wykonywana aż do linii zawierającej ciąg liter stop (włącznie). Należy zwrócic uwagę, że sed nie szuka wystąpienia stop w linii, w której wcześniej dopasował start, tylko dopiero w liniach następnych po niej. Analogicznie nie szuka już wystapienia start w linii, w której wczesniej dopasował stop. sed ’/start/,/stop/ s/#.*//’ Przyszedł czas na poznanie kolejnych komend seda. Komenda d kasuje linię odpowiadającą zawężeniu: sed sed sed sed ’11,$ d’ <file (usuwa linie od 11-tej do ostatniej) ’/^#/ d’ (usuwa puste linie) ’s/^#.*//’ (usuwa linie zaczynające się od _#_) ’s/#.*//; /^$/ d’ (kasowanie zawartości linii po _#_ i kasowanie pustych linii) Komenda p — drukowanie: sed -n ’1,10 p’ sed -n ’/match/ p’ Symbol ! odwraca podane zawężenie: sed -n ’/match/ !p’ (drukujemy linie w których nie występuje wzorzec /match/) Komenda q przerywa działanie programu. W przykładzie poniżej wtedy, gdy dociera do 11-ej linii. sed ’11 q’ Grupowanie przy pomocy nawiasów klamrowych pozwala stosować zawężenie do sekwencji komend, a także zagnieżdżać zawężenia i pisać bardziej skomplikowane skrypty. Następny przykład to "krótki program", który pomiędzy liniami z ’begin’ oraz ’end’, które są pomiędzy ’start’ i ’stop’ usuwa wszystkie linie zaczynające się od #. Tak, jak wcześniej wspominaliśmy całe polecenie seda działa dla każdej linii z osobna tzn. każda z czterech komend w nabardziej wewnętrznym nawiasie klamrowym wykonana jest na tej samej linii, która w tym czasie jest przechowywana w pattern space. Najpierw zastępujemy całą linię zaczynającą się od # przez znak pusty. Ponieważ . nie dopasowuje się do \n musimy wykasować pustą linię. To robimy kolejnej linii. Komenda p jest potrzebna do drukowania ze względu na flagę -n. #!/bin/bash # This script removes #-type comments # between ’begin’ and ’end’ words, # located between ’start’ and ’stop’. Strona 8/9 Środowisko programisty Zestaw 2 Semestr zimowy 2016/2017 Kraków, 15-16 października 2015 sed -n ’ /start/,/stop/ { /begin/,/end/ { s/#.*// /^$/ d p } }’ W kolejnym przykładzie zamieniamy każde wystąpienie old na wystąpienie new pomiędzy liniami zawierającym begin oraz end, ale nie w tych liniach. #!/bin/bash sed ’ /begin/,/end/ { /begin/ !{ /end/ !{ s/old/new/ }} }’ Komenda :s w edytorze vim :[range]s[ubstitute]/{pattern}/{string}/[flags] [count] Dla każdej linii w zawężeniu: {range} polecenie :s[ubstitute] zastępuje {pattern} przez {string}. Zasięg można definiować tak, jak w sedzie. Można podać pojedynczą linię, albo przedział linii. Często używany jest skrót dla 1,$ to %. Poza tym można używać bookmarków: ’c lub ’C w definiowaniu zawężenia. W przypadku kiedy podany jest [count] zastępowanie następuje w [count] liniach, licząc od końca zawężęnia. Przykładową flagą w flags jest g, która podobnie, jak w sedzie pozwala na zastępowanie wszystkich wystąpień w rozpatrywanej linii. Więcej o poleceniu można dowiedzieć się używając vimowskiej komendy: :help. Strona 9/9