Jak pisać niezależne skrypty awk?



Pobieranie 70.93 Kb.
Data03.05.2016
Rozmiar70.93 Kb.

http://ptm.linux.pl/man_HTML/info_pl_HTML/gawk/gawk_toc.html)


Język awk jest bardzo użyteczny w tworzeniu raportów z ogromnych ilości danych, takich jak dane sumaryczne z wyjścia innych programów narzędziowych, jak np. ls.

Jak pisać niezależne skrypty awk?

Wykorzystujemy mechanizm skryptowy ‘#!’. Wiersz rozpoczynający się od #! wyszczególnia pełną nazwę interpretera, jaki ma zostać uruchomiony, i opcjonalny argument początkowy wiersza poleceń, jaki ma zostać przesłany do interpretera. System operacyjny uruchamia wówczas interpreter z zadanym argumentem i pełną listą argumentów wykonywanego programu. Pierwszy argument listy jest pełną nazwą pliku programu awk. Reszta listy argumentów będzie albo opcjami awk, albo plikami danych, albo oboma.

Na przykład piszemy w pliku tekstowym plik_reg:

#! /bin/awk -f


BEGIN { print "Zaczynamy!" }

{print $0}

Po uczynieniu pliku plik_reg plikiem wykonywalnym (narzędziem chmod) piszemy

plik_reg plik_tekst

w wierszu powłoki, przy czym plik_tekst jest przetwarzanym plikiem tekstowym. Wówczas system uruchomi awk.

Uwaga. Jeśli nie chcemy wykorzystywać #! (czyli nie wpiszemy do pliku wiersza: #! /bin/awk -f) uruchomienie musi nastąpić w poniższy sposób:

awk -f plik_reg plik_tekst



Przykłady



#skrypt wypisuje wszystkie pola tekstowego zawierające wzorzec foo

/foo/ { print $0 }





#skrypt sumuje wartości piątego pola wszystkich linii pliku tekstowego, #których wartość pola szóstego wynosi Nov i sumę wypisuje na wyjściu

$6 == "Nov" { sum += $5 }

END { print sum }'


  1. Bardzo prosty skrypt - proszę zinterpretować jego działanie.

BEGIN {print "Początek"}

$1 == "Linux" {print $0}

$2 > 10 {print "wartość pola wynosi %d", $2}

END {print "Koniec”}





#skrypt wypisuje dziewiąte pole każdej takiej linii pliku #tekstowego, dla której wartość pola piątego wynosi 1024

$5 == 1024 {print $9}




  1. Separatory wejścia i wyjścia

#skrypt wypisuje wszystkie linie pliku tekstowego zawierające #łańcuch ate - separatorem rekordów na wyjściu jest kropka

BEGIN {ORS="."}

/ate/{print}



  1. Separatory wejścia i wyjścia (cd.)

Praktycznym przykładem pliku danych zorganizowanego w ten sposób może być lista adresowa, gdzie każda pozycja oddzielona jest pustymi wierszami. Załóżmy, że mamy taką listę w pliku `addresses':

Jane Doe


123 Main Street

Anywhere, SE 12345-6789


John Smith

456 Tree-lined Avenue

Smallville, MW 98765-4321

...


Prosty program do przetwarzania tych danych może być taki:

# addrs.awk --- prosty program listy adresowej


# Rekordy oddzielone są pustymi wierszami

# każdy wiersz jest jednym polem.

BEGIN { RS = "" ; FS = "\n" }
{

print "Name is:", $1

print "Address is:", $2

print "City and State are:", $3

print ""

}
BEGIN {print "Sumuj"}

{printf "Liczba rekordow: %g, numer rekordu: %g\n", NF, FNR}

{suma +=$5}

END {printf "\nSuma: %g kB\n", suma/1024}

Uruchomienie programu daje następujące wyjście:

$ awk -f addrs.awk addresses

-| Name is: Jane Doe

-| Address is: 123 Main Street

-| City and State are: Anywhere, SE 12345-6789

-|

-| Name is: John Smith



-| Address is: 456 Tree-lined Avenue

-| City and State are: Smallville, MW 98765-4321

-|


  1. Separatory wejścia i wyjścia (cd.)

#Skrypt wypisuje pierwsze i ostatnie pole każdego rekordu #wejściowego, rozdzielając je średnikami, z pustym wierszem dodanym #po każdym wierszu

BEGIN { OFS = ";"; ORS = "\n\n" }

>{print $1, $2 }



  1. Separatory wejścia i wyjścia (cd.)

Zdarza się, że chcemy badać każdy znak rekordu z osobna. Wtedy w gawk (nie awk) po prostu przypisujemy pusty łańcuch ("") do FS. W tym przypadku, każdy pojedynczy znak rekordu stanie się odrębnym polem. Oto przykład:

BEGIN { FS = "" }

{

for (i = 1; i <= NF; i = i + 1)



print "W polu", i, "jest", $i

}


  1. Separatory wyjścia (cd.)

Instrukcja printf nie dołącza samoczynnie znaku nowej linii do tworzonego wyjścia. Wypisuje tylko to, co wyszczególnia łańcuch formatu. Zatem, jeżeli chcemy znaku nowej linii, to musimy go zawrzeć w łańcuchu formatu. Zmienne separatorów wyjścia OFS and ORS nie mają wpływu na instrukcje printf. Na przykład:

BEGIN {


ORS = "\nAU!\n"; OFS = "!"

msg = "Nie panikuj!"; printf "%s\n", msg

}


  1. Polecenie getline

#przykład zamienia miejscami każde dwa wiersze wejścia.

{

if ((getline tmp) > 0) {



print tmp

print $0


} else

print $0


}


  1. Polecenie getline (cd.)

# program napotkając pierwsze pole o wartości 10 w bieżącym pliku #wejściowym czyta rekord wejściowy z pliku `secondary.input'.

{

if ($1 == 10) {



getline < "secondary.input"

print


} else

print


}


  1. Polecenie getline (cd.)

#program kopiuje wszystkie pliki wejściowe na wyjście, za wyjątkiem #rekordów zawierających `@include nazwapliku'. Taki rekord zastępowany #jest zawartością pliku nazwapliku.

{

if (NF == 2 && $1 == "@include") {



while ((getline line < $2) > 0)

print line

close($2)

} else


print

}


  1. Polecenie getline (cd.)

#Polecenie getline bez argumentów służy do czytania wejścia z bieżącego #pliku wejściowego. W tym przypadku odczytuje ono tylko kolejny rekord #wejściowy i rozbija go na pola. Przydatne, gdy zakończyliśmy #przetwarzanie bieżącego rekordu, ale chcemy od razu wykonać specjalne #przetwarzanie następnego.

#Program usuwa z wejścia wszystkie komentarze typu używanego w C, `/* #... */'

{

if ((t = index($0, "/*")) != 0) {



# wartością będzie "" jeśli t jest 1

tmp = substr($0, 1, t - 1)

u = index(substr($0, t + 2), "*/")

while (u == 0) {

if (getline <= 0) {

m = "unexpected EOF or error"

m = (m ": " ERRNO)

print m > "/dev/stderr"

exit

}

t = -1



u = index($0, "*/")

}

# wyrażenie będzie równe "" jeśli */



# pojawiło się na końcu wiersza

$0 = tmp substr($0, t + u + 3)

}

print $0


}

  1. Tablice

#Program pobiera listę wierszy, każdy zaczynający się numerem #wiersza, i wypisuje je w kolejności tych numerów. Numery wierszy nie #są uporządkowane. Program sortuje wiersze tworząc tablicę z #wykorzystującą numery wierszy jako indeksy. Następnie wypisuje #wiersze w kolejności ich posortowanych numerów.

{

if ($1 > max)



max = $1

arr[$1] = $0

}
END {

for (x = 1; x <= max; x++)

print arr[x]

}


  1. Tablice (cd.)

#Program wypisuje liczbę wystąpień każdego słowa ze #swojego wejścia. #Przez wykorzystanie łańcuchów jako indeksów ilustruje skojarzeniową #naturę tablic awk. Demonstruje także konstrukcję `for x in tablica'. #Pokazuje, w jaki sposób można wykorzystać awk w połączeniu z innymi #programami narzędziowymi do wykonania stosunkowo złożonych, #użytecznych zadań przy minimum wysiłku.


{

for (i = 1; i <= NF; i++)

czest[$i]++

}
END {

for (slowo in czest)

printf "%s\t%d\n", slowo, czest[slowo]

}'

Powyższy program zawiera dwie reguły. Pierwsza, ponieważ ma pusty wzorzec, wykonywana jest na każdym wierszu wejścia. Wykorzystuje dostępny w awk mechanizm dostępu do pól do wyłuskania z wiersza poszczególnych słów, a zmienną wbudowaną NF do rozpoznania, ile jest dostępnych pól.



Dla każdego pola wejściowego zwiększany jest element tablicy czest, by odzwierciedlał, że wyraz ten widziano kolejny raz.

Druga reguła, ponieważ ma wzorzec END, nie jest wykonywana aż do momentu wyczerpania wejścia. Wypisuje ona zawartość tablicy czest, która została skonstruowana wewnątrz pierwszej akcji.




  1. Tablice (cd.)

Problem. Jak usunąć powtarzające się wiersze z pliku danych zachowując kolejność wierszy? Dobrym przykładem może tu być plik historii poleceń powłoki. Plik historii przechowuje kopię każdego wprowadzonego polecenia, a nie jest niczym nietypowym kilkakrotne powtarzanie tego samego polecenia. Chcemy od czasu do czasu kondensować plik historii przez usunięcie powielonych pozycji.

Zadanie takie wykonuje poniższy prosty program. Wykorzystuje dwie tablice. Tablica dane indeksowana jest tekstem każdego wiersza. Dla każdego wiersza inkrementowane jest dane[$0].

Jeśli jakiegoś konkretnego wiersza nie napotkano wcześniej, to dane[$0] będzie zerem. W tym przypadku, jego tekst zapamiętywany jest w wiersze[ile]. Każdy element w tablicy wiersze jest niepowtarzalnym poleceniem, a indeksy tej tablicy wskazują na kolejność w jakiej napotkano te wiersze. Reguła wypisuje po prostu wiersze, po kolei.
# histsort.awk --- upakowanie pliku historii powłoki

# Arnold Robbins, arnold@gnu.org, Public Domain

# May 1993
# Dzięki Byronowi Rakitzis za ogólny pomysł

{

if (dane[$0]++ == 0)



wiersze[++ile] = $0

}
END {

for (i = 1; i <= ile; i++)

print wiersze[i]

}

Funkcje łańcuchowe.

gensub(regex, zastąp, jak [, cel])

Jeżeli jak jest łańcuchem zaczynającym się od `g' lub `G', to wszystkie dopasowania regex w cel zastępowane są przez zastąp. W przeciwnym razie, zastępowane jest jakie kolejne wystąpienie. Jeżeli nie podano celu, to używane jest $0. Wartością zwracaną jest zmieniony łańcuch -- oryginalny cel nie jest modyfikowany. Wewnątrz zastąp, do wskazania tekstu pasującego do n-tego podwyrażenia w nawiasach stosuje się `\n', gdzie n jest cyfrą od zera do dziewięciu. Funkcja gensub jest specyficzna dla gawk.

gsub(regex, zastąp [, cel])

Zastępuje każdy podłańcuch łańcucha cel pasujący do wyrażenia regularnego regex łańcuchem zastąp. Zwraca liczbę zastąpień. Jeżeli nie podano celu, używane jest $0.

index(łańc, szuk)

Zwraca pozycję łańcucha szuk w łańcuchu łańc, lub zero jeśli nie występuje szuk.
BEGIN { print index("peanut", "an") }

length([łańc])

Zwraca długość łańcucha łańc. Jeżeli nie podano argumentu, zwracana jest długość $0.

match(łańc, regex)

Zwraca pozycję w łańc, na której pojawia się wyrażenie regularne regex, albo zero jeśli regex nie występuje, i nadaje wartości zmiennym RSTART i RLENGTH.
{

if ($1 == "FIND")

regex = $2

else {


gdzie = match($0, regex)

if (gdzie != 0)

print "Dopasowanie", regex, \

"znaleziono na pozycji", \

gdzie, "w", $0

}

}


split(łańc, tabl [, regex])

Dzieli łańcuch na kawałki rozdzielane przez wyrażenie regularne regex i umieszczane w tablicy tabl. Zwraca liczbę elementów. Jeżeli pominięto regex, to zamiast niego używane jest FS. regex może być łańcuchem pustym, co spowoduje umieszczenie każdego znaku w osobnym elemencie tablicy. Na początku wymazywana jest tablica tabl.

split("cul-de-sac", a, "-")

rozbija łańcuch `cul-de-sac' na trzy pola, wykorzystując `-' jako separator. Nadaje tablicy a zawartość jak niżej:

a[1] = "cul"

a[2] = "de"

a[3] = "sac"

Wartością zwracaną przez to wywołanie split jest trzy. Tak jak przy podziale na pola, gdy wartością sep-pól jest " ", początkowe i końcowe białe znaki są ignorowane, a elementy rozdzielane są przez ciągi białych znaków. Również tak jak przy podziale na pola, jeżeli sep-pól jest łańcuchem pustym, każdy z poszczególnych znaków dzielonego łańcucha tworzy odrębny element tablicy.

sprintf(fmt, lista-wyr)

Wypisuje listę-wyr zgodnie z fmt i zwraca wynikowy łańcuch.


sprintf("pi = %.2f (w przybl.)", 22/7)

zwraca łańcuch "pi = 3.14 (w przybl.)".

sub(regex, zastąp [, cel])

Tak jak gsub, ale zastępowany jest tylko pierwszy pasujący podłańcuch.


substr(łańc, indeks [, ])

Zwraca -znakowy podłańcuch łańcucha łańc zaczynający się od pozycji indeks. Jeżeli pominięto , używana jest reszta łańc.

tolower(łańc)

Zwraca kopię łańcucha łańc ze wszystkimi dużymi znakami zamienionymi na odpowiadające im małe. Znaki niealfabetyczne pozostawione są bez zmian.



toupper(łańc)

Zwraca kopię łańcucha łańc ze wszystkimi małymi znakami zamienionymi na odpowiadające im duże. Znaki niealfabetyczne pozostawione są bez zmian.


©absta.pl 2016
wyślij wiadomość

    Strona główna