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

Podobne dokumenty