Home Dokumentacje Efektywne programowanie w AWK - Podręcznik użytkownika GNU awk - Funkcje wbudowane
05 | 12 | 2019
Efektywne programowanie w AWK - Podręcznik użytkownika GNU awk - Funkcje wbudowane Drukuj

Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.

 


 

12. Funkcje wbudowane

Funkcje wbudowane to funkcje, które są zawsze dostępne do wywołania w programie awk. Ten rozdział opisuje wszystkie funkcje wbudowane występujące w awk. O niektórych z nich wspomniano w innych sekcjach, ale tu dla wygody zestawiono je wszystkie. (Możliwe jest też samodzielne definiowanie nowych funkcji. Zob. 13. Funkcje definiowane przez użytkownika.)

12.1. Wywoływanie funkcji wbudowanych

Aby wywołać funkcję wbudowaną należy napisać nazwę funkcji, po której w nawiasach wystąpią argumenty. Na przykład, `atan2(y + z, 1)' jest wywołaniem funkcji atan2, z dwoma argumentami.

Biały znak między nazwą funkcji wbudowanej a nawiasem otwierającym jest ignorowany, ale zaleca się unikanie stosowania go w tym miejscu. Funkcje definiowane przez użytkownika nie zezwalają na używanie w ten sposób białego znaku, a łatwiej jest uniknąć pomyłek przestrzegając prostej konwencji, która zawsze działa: żadnych białych znaków po nazwie funkcji.

Każda z funkcji wbudowanych przyjmuje pewną liczbę argumentów. W niektórych przypadkach argumenty mogą być pomijane. Domyślne wartości pominiętych argumentów są różne, w zależności od funkcji. Opisano je przy omówieniach poszczególnych funkcji. W niektórych implementacjach awk dodatkowe argumenty przekazywane funkcjom wbudowanym są ignorowane. Jednak w gawk jest to błąd krytyczny.

Przy wywoływaniu funkcji wyrażenia tworzące jej parametry faktyczne są obliczane przed wykonaniem wywołania. Na przykład, w tym fragmencie kodu:

i = 4
j = sqrt(i++)

zmienna i otrzymuje wartość pięć przed wywołaniem sqrt z wartością cztery jako jej aktualnym parametrem.

Kolejność obliczanie wyrażeń stosowanych jako argumenty funkcji jest niezdefiniowana. Zatem, nie powinno się pisać programów, które zakładają, że argumenty są obliczane od lewej do prawej czy od prawej do lewej. Na przykład,

i = 5
j = atan2(i++, i *= 2)

Jeżeli kolejność obliczeń będzie od lewej do prawej, to i najpierw stanie się równe 6, potem 12, a atan2 zostanie wywołane z tymi dwoma argumentami: 6 i 12. Ale jeśli obliczenia będą wykonywane od prawej do lewej, to i najpierw stanie się równe 10, następnie 11, a atan2 wywołane zostanie z argumentami 11 i 10.

12.2. Wbudowane funkcje numeryczne

Oto pełna lista funkcji wbudowanych działających na liczbach. Parametry opcjonalne umieszczono w nawiasach kwadratowych ("[" i "]").

int(x)
Tworzy najbliższą liczbę całkowitą w stosunku do x, położoną pomiędzy x a zerem, obciętą w stronę zera. Na przykład, int(3) jest trzy, int(3.9) jest trzy, int(-3.9) jest -3, a int(-3) jest także -3.
sqrt(x)
Podaje pierwiastek kwadratowy z x. Zgłasza błąd gdy x jest ujemne. Zatem, sqrt(4) wynosi dwa.
exp(x)
Daje potęgę naturalną o wykładniku x (e ^ x), lub zgłasza błąd jeśli x jest poza dopuszczalnym zakresem. Zakres wartości, jakie może przyjmować x zależy od reprezentacji zmiennoprzecinkowej danej architektury.
log(x)
Podaje logarytm naturalny z x, jeśli x jest dodatnie; w przeciwnym razie, zgłasza błąd.
sin(x)
Daje sinus z x, gdzie x jest w radianach.
cos(x)
Daje cosinus z x, gdzie x jest w radianach.
atan2(y, x)
Podaje arcus tangens z y / x w radianach.
rand()
Daje liczbę losową. Wartości rand są równomiernie rozłożone między zerem a jeden. Wartość nigdy nie wynosi zero ani jeden. Często zamiast tego potrzebujemy losowych liczb całkowitych. Oto funkcja użytkownika, która można wykorzystać do uzyskiwania nieujemnych liczb całkowitych mniejszych niż n:
function randint(n) {
     return int(n * rand())
}
Mnożenie tworzy losową liczbę większą od zera a mniejszą od n. Następnie robimy z niej całkowitą (za pomocą int) pomiędzy zero a n - 1, włącznie. Oto przykład, w którym podobną funkcję zastosowano do tworzenia liczb pseudolosowych między jeden a n. Program ten wypisuje nową liczbę losową dla każdego rekordu wejściowego.
awk '
# funkcja rzucająca symulowaną kostką.
function roll(n) { return 1 + int(rand() * n) }

# Rzuć trzema 6-bocznymi kostkami i
# wypisz całkowitą liczbę punktów.
{
      printf("%d punktów\n",
             roll(6)+roll(6)+roll(6))
}'
Uwaga: W większości implementacji awk, łącznie z gawk, rand zaczyna tworzenie liczb losowych od tej samej liczby początkowej (czyli ziarna) przy każdym uruchomieniu awk. Zatem, dany program będzie przy każdym uruchomieniu generował te same wyniki. Liczby te są losowe w pojedynczym przebiegu awk, ale przewidywalne między uruchomieniami. Jest to przydatne przy debugowania, ale jeśli chcemy, by przy każdym użyciu program robił inne rzeczy, musimy zmienić ziarno na wartość, która przy każdym przebiegu będzie inna. W tym celu należy skorzystać z srand.
srand([x])
Funkcja srand nadaje punktowi startowemu, ziarnu, tworzenia liczb losowych wartość x. Każda wartość ziarna prowadzi do konkretnego ciągu liczb losowych.(11) Zatem, jeżeli kolejnym razem przypiszemy ziarnu tę samą wartość, otrzymamy ponownie ten sam ciąg liczb losowych. Jeżeli argument x zostanie pominięty, jak w srand(), to jako ziarno zostaną użyte bieżąca data i czas. Jest to sposób uzyskiwania liczb losowych, które naprawdę są nieprzewidywalne. Wartością zwracaną przez srand jest poprzednie ziarno. Ułatwia to śledzenie ziaren przy konsekwentnym odtwarzaniu sekwencji liczb losowych.

12.3. Funkcje wbudowane działające na łańcuchach

Funkcje w tej sekcji badają lub zmieniają tekst jednego lub więcej łańcuchów. Parametry opcjonalne umieszczono w nawiasach kwadratowych ("[" i "]").

index(gdzie, co)
Szuka w łańcuchu gdzie pierwszego wystąpienia łańcucha co i zwraca pozycję, w znakach, na której pojawia się w gdzie to wystąpienie. Na przykład:
$ awk 'BEGIN { print index("peanut", "an") }'
-| 3
Jeżeli nie znaleziono co, to index zwraca zero. (Należy pamiętać, że w awk indeksy w łańcuchach zaczynają się od jeden.)
length([łańcuch])
Podaje liczbę znaków w łańcuchu. Jeżeli łańcuch jest liczbą, zwracana jest długość łańcucha numerycznego reprezentującego tę liczbę. Na przykład, length("abcde") wynosi pięć. W przeciwieństwie do tego, length(15 * 35) wynosi trzy. W jaki sposób? Cóż, 15 * 35 = 525, a 525 jest zamieniane na łańcuch "525", który ma trzy znaki. Jeżeli nie podano argumentu, length zwraca długość $0. W starszych wersjach awk, można było wywoływać funkcję length bez żadnych nawiasów. Robienie tego w standardzie POSIX oznaczono jako "niezalecane". To znaczy, że mimo, iż można tak robić we własnych programach, jest to cecha, która ostatecznie zostanie usunięta z przyszłych wersji standardu. Z tego powodu, dla maksymalnej przenośności programów awk, powinno się zawsze podawać nawiasy.
match(łańcuch, regexp)
Funkcja match szuka w łańcuchu łańcuch najdłuższego lewego podłańcucha pasującego do wyrażenia regularnego regexp. Zwraca pozycję znaku, indeks, gdzie rozpoczyna się ten podłańcuch (jeden, jeśli zaczyna się na początku łańcucha). Jeżeli nie znaleziono dopasowania zwracane jest zero. Funkcja match nadaje zmiennej wbudowanej RSTART wartość znalezionego indeksu. Przypisuje też zmiennej wbudowanej RLENGTH długość, w znakach, pasującego podłańcucha. Jeżeli nie znaleziono dopasowania, RSTART jest zerowane, a RLENGTH otrzymuje wartość -1. Na przykład:
awk '{
       if ($1 == "FIND")
         regex = $2
       else {
         gdzie = match($0, regex)
         if (gdzie != 0)
           print "Dopasowanie", regex,        \
                    "znaleziono na pozycji", \
                    gdzie, "w", $0
       }
}'
Powyższy program szuka wierszy pasujących do wyrażenia regularnego zapamiętanego w zmiennej regex. To wyrażenie regularne można zmienić. Jeśli pierwszym słowem wiersza jest `FIND', to regex zmieniane jest na drugie słowo tego wiersza. Stąd też, przy podanym:
FIND dzia+ła
Mój program działa
ale niezbyt szybko
FIND Melvin
JF+KM
Ta linijka jest własnością Reality Engineering Co.
Melvin był tutaj.
awk wypisze:
Dopasowanie dzia+ła znaleziono na pozycji 12 w Mój program działa
Dopasowanie Melvin znaleziono na pozycji 1 w Melvin był tutaj.
split(łańcuch, tablica [, sep-pól])
Dzieli łańcuch na kawałki rozdzielane przez sep-pól i zapamiętuje je w tablicy. Pierwszy z nich zapamiętywany jest w array[1], drugi w array[2], i tak dalej. Wartość łańcuchowa trzeciego argumentu, sep-pól, jest wyrażeniem regularnym opisującym, w którym miejscach dzielić łańcuch (choć jak FS może być wyrażeniem regularnym opisującym, w których miejscach dzielić rekordy wejściowe). Jeżeli pominięto sep-pól, stosowana jest wartość FS. split zwraca liczbę utworzonych elementów. Funkcja split dzieli łańcuchy na części w sposób podobny do tego, w jaki wiersze wejściowe dzielone są na pola. Na przykład:
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. (Jest to rozszerzenie specyficzne dla gawk.) W ostatnich implementacjach awk, łącznie z gawk, trzeci argument może być równie dobrze łańcuchem, jak i stałym wyrażeniem regularnym (/abc/) (c.k.). Standard POSIX również na to pozwala. Przed podziałem łańcucha split usuwa wszelkie uprzednio istniejące elementy tablicy tablica (c.k). Jeżeli łańcuch w ogóle nie zawiera dopasowania do sep-pól, tablica będzie mieć jeden element. Wartością tego elementu będzie pierwotny łańcuch.
sprintf(format, wyrażenie1,...)
Zwraca (bez wypisywania) łańcuch jaki wypisałaby instrukcja printf z tymi samymi argumentami (zob. 6.5. Wymyślne wyjście dzięki instrukcji printf). Na przykład:
sprintf("pi = %.2f (w przybl.)", 22/7)
zwraca łańcuch "pi = 3.14 (w przybl.)".
sub(regexp, zastąpienie [, cel])
Funkcja sub zmienia wartość celu. Szuka w tej wartości, traktowanej jako łańcuch, lewego najdłuższego podłańcucha pasującego do wyrażenia regularnego, regexp, rozszerzając to dopasowanie, tak dalece, jak to możliwe. Następnie cały łańcuch jest zmieniany poprzez zastąpienie dopasowanego tekstu przez zastąpienie. Zmodyfikowany łańcuch staje się nową wartością celu. Jest to szczególna funkcja, gdyż cel nie jest używane po prostu do obliczenia wartości, i nie wystarczy tu dowolne wyrażenie: musi to być zmienna, pole lub element tablicy, tak by sub mogła w nim przechować zmienioną wartość. Jeżeli pominięto ten argument, to domyślnie wykorzystywane i zmieniane jest $0. Na przykład:
str = "water, water, everywhere"
sub(/at/, "ith", str)
Nadaje str wartość "wither, water, everywhere", dzięki zamianie lewego najdłuższego wystąpienia `at' przez `ith'. Funkcja sub zwraca liczbę wykonanych zastąpień (albo jeden albo zero). Jeżeli w zastąpieniu pojawia się znak specjalny `&', to oznacza on ten sam podłańcuch, który pasował do regexp. (Jeżeli do danego wyrażenia regularnego może pasować więcej niż jeden łańcuch, wówczas pasujący w danym przypadku łańcuch może być różny.) Na przykład:
awk '{ sub(/kandydat/, "& i jego żona"); print }'
zmienia w każdym wierszu wejściowym pierwsze wystąpienie słowa `kandydat' na `kandydat i jego żona'. Oto inny przykład:
awk 'BEGIN {
        str = "daabaaa"
        sub(/a*/, "C&C", str)
        print str
}'
-| dCaaCbaaa
Pokazuje on, jak `&' może reprezentować łańcuch nie będący stałą. Ilustruje też regułę "lewe, najdłuższe" w dopasowaniach wyrażeń regularnych. (zob. 4.6. Jak bardzo pasuje tekst?). Działanie tego znaku specjalnego (`&') można wyłączyć stawiając przed nim w łańcuchu odwrotny ukośnik. Jak zwykle, w celu wstawienia odwrotnego ukośnika w łańcuchu, musimy napisać dwa odwrotne ukośniki. Stąd też, aby w argumencie zastąpienie umieścić dosłowny `&', należy napisać `\\&' w stałej łańcuchowej. Na przykład, w ten sposób zastępujemy pierwszy znak `|' w każdym wierszu znakiem `&':
awk '{ sub(/\|/, "\\&"); print }'
Zauważ: jak wspomniano wyżej, trzeci argument sub musi być zmienną, polem lub odwołaniem do tablicy. Niektóre z wersji awk pozwalają, by argument ten był wyrażeniem nie będącym lwartością. W takim przypadku, sub nadal szuka wzorca i zwraca zero lub jeden, ale wynik podstawienia (jeśli jest takowy) zostanie odrzucony, gdyż nie ma miejsca, w którym możnaby go umieścić. Te wersje awk akceptują wyrażenia takie jak to:
sub(/USA/, "United States", "USA i Canada")
Z uwagi na zgodność historyczną, gawk przyjmie błędny kod, jak w powyższym przykładzie. Jednak użycie jakiegokolwiek innego nie dającego się zmienić obiektu jako trzeciego parametru spowoduje błąd krytyczny, a program nie uruchomi się. Na koniec, jeśli regexp nie jest stałym wyrażeniem regularnym, to jest przekształcane na łańcuch. Następnie wartość tego łańcucha traktowana jest jak wyrażenie regularne do dopasowania.
gsub(regexp, zastąpienie [, cel])
Podobna do funkcji sub, poza tym, że gsub zastępuje wszystkie najdłuższe, lewe, nie nakładające się pasujące podłańcuchy jakie znajdzie. Litera `g' w gsub znaczy "globalnie", czyli zastępowanie wszędzie. Na przykład:
awk '{ gsub(/Britain/, "United Kingdom"); print }'
zastępuje we wszystkich rekordach wejściowych wszystkie wystąpienia łańcucha `Britain' przez `United Kingdom'. Funkcja gsub zwraca liczbę wykonanych podstawień. Jeżeli pominięto zmienną, jaka ma być przeszukana i zmieniona, cel, to używany jest cały rekord wejściowy, $0. Jak w sub, znaki `&' i `\' mają specjalne znaczenie, a trzeci argument musi być lwartością.
gensub(regexp, zastąpienie, jak [, cel])
gensub jest ogólną funkcją podstawiania. Podobnie jak sub i gsub, przegląda łańcuch docelowy cel szukając dopasowań wyrażenia regularnego regexp. Inaczej niż w sub i gsub, zmodyfikowany łańcuch zwracany jest jako wynik działania funkcji, a pierwotny łańcuch nie jest zmieniany. Jeżeli jak jest łańcuchem zaczynającym się literą `g' lub `G', to przez zastąpienie zostaną zastąpione wszystkie dopasowania regexp. W przeciwnym razie, how jest liczbą wskazującą, które wystąpienie regexp ma zostać wymienione. Jeżeli nie podano celu, to zamiast niego wykorzystywane jest $0. gensub udostępnia dodatkową cechę, niedostępną w sub czy gsub: możliwość określenia składowych wyrażenia regularnego w zastępującym tekście. Robimy to używając nawiasów do wskazania składowych w dopasowywanym wyrażeniu regularnym, a następnie podanie `\n' w tekście zastępującym, gdzie n jest cyfrą od jeden do dziewięć. Na przykład:
$ gawk '
> BEGIN {
>      a = "abc def"
>      b = gensub(/(.+) (.+)/, "\\2 \\1", "g", a)

>      print b
> }'
-| def abc
Jak opisano wyżej przy sub, musimy wpisać dwa odwrotne ukośniki, by w łańcuchu uzyskać jeden. W tekście zastępującym sekwencja `\0' reprezentuje cały dopasowany tekst, tak samo jak znak `&'. Ten przykład pokazuje, jak wykorzystać trzeci argument do wyboru dopasowania, które ma zostać zmienione.
$ echo a b c a b c |

> gawk '{ print gensub(/a/, "AA", 2) }'
-| a b c AA b c
W tym przypadku zastosowano $0, jako domyślny łańcuch docelowy. gensub zwraca jako wynik nowy łańcuch, przekazywany bezpośrednio do print w celu wypisania. Jeśli argument jak jest łańcuchem nie rozpoczynającym się od `g' lub `G', lub jeśli jest liczbą mniejszą od zera, wykonywane jest tylko jedno zastąpienie. Jeżeli w cel nie ma dopasowania do regexp, wartością zwracaną przez gensub jest pierwotna, niezmieniona wartość celu. gensub jest rozszerzeniem gawk; nie jest dostępne w trybie zgodności (zob. 14.1. Opcje wiersza poleceń).
substr(łańcuch, start [, długość])
Zwraca długość-znakowy podłańcuch łańcucha, poczynając od znaku numer start. Pierwszy znak każdego łańcucha jest znakiem numer jeden. Na przykład, substr("washington", 5, 3) zwraca "ing". Jeżeli nie podano długości, funkcja zwraca całą końcówkę łańcucha rozpoczynającą się od znaku numer start. Na przykład, substr("washington", 5) zwraca "ington". Cała końcówka zwracana jest także jeśli długość jest większa niż liczba znaków pozostałych w danym łańcuchu, licząc od znaku numer start. Zauważ: Do łańcucha zwracanego przez substr nie można wykonać przypisania wartości. A zatem błędem jest próba zmiany części łańcucha w ten sposób:
string = "abcdef"
# próba uzyskania "abCDEf", nie zadziała
substr(string, 3, 3) = "CDE"
lub użycia substr jako trzeciego argumentu sub czy gsub:
gsub(/xyz/, "pdq", substr($0, 5, 20))  # ŹLE
tolower(łańcuch)
Zwraca kopię łańcucha, w której wszystkie duże litery zamieniono na odpowiadające im małe litery. Znaki niealfabetyczne są pozostawione bez zmian. Na przykład, tolower("MiEsZaNe 123") zwraca "mieszane 123".
toupper(łańcuch)
Zwraca kopię łańcucha, w której wszystkie małe litery zamieniono na odpowiadające im duże litery. Znaki niealfabetyczne są pozostawione bez zmian. Na przykład, tolower("MiEsZaNe 123") zwraca "MIESZANE 123".

12.3.1. Więcej o `\' i `&' w sub, gsub i gensub

Przy stosowaniu sub, gsub lub gensub, i usiłowaniu uzyskania dosłownych znaków odwrotnego ukośnika i ampersandu w tekście zastępującym, należy pamiętać o istnieniu kilku poziomów przetwarzania sekwencji specjalnych, jakie zostaną wykonane.

Po pierwsze, mamy poziom leksykalny, występujący wtedy, gdy awk czyta nasz program i tworzy jego wewnętrzną kopię, nadającą się do wykonania.

Następny poziom pojawia się podczas wykonywania programu, gdy awk rzeczywiście bada łańcuch zastępujący, by określić, co ma utworzyć.

Na obu tych poziomach awk szuka zdefiniowanego zestawu znaków, jakie mogą pojawić się po odwrotnym ukośniku. Na poziomie leksykalnym, szukane są sekwencje specjalne wymienione w 4.2. Sekwencje specjalne. Stąd też, dla każdego `\', który awk ma przetworzyć na poziomie wykonania, zapisujemy dwa `\' na poziomie leksykalnym. Gdy po `\' występuje znak nie tworzący z nim poprawnej sekwencji specjalnej, zarówno uniksowy awk jak i gawk po prostu usuwają taki początkowy `\', i umieszczają następujący po nim znak w łańcuchu. Zatem, na przykład, "a\qb" traktowane jest jak "aqb".

Na poziomie wykonania rozmaite funkcje różnie obsługują sekwencje `\' i `&'. Sytuacja jest (niestety) dość złożona.

Historycznie, funkcje sub i gsub traktowały w specjalny sposób dwuznakową sekwencję `\&'. W tworzonym tekście była ona zastępowana pojedynczym `&'. Wszystkie inne `\' w łańcuchu zastąpienia, które nie poprzedzały znaku `&' były przekazywane dalej bez zmian. Ilustruje to tabela:

Tabela ta pokazuje zarówno przetwarzanie na poziomie leksykalnym, gdzie nieparzysta liczba odwrotnych ukośników staje się parzystą liczbą na poziomie wykonania, jak i wykonywane przez sub przetwarzanie na poziomie wykonania. (Dla uproszczenia, reszta tabel poniżej pokazuje tylko przypadek wprowadzenia na poziomie leksykalnym parzystej liczby odwrotnych ukośników `\'.)

Kłopot przy podejściu historycznym stanowi to, że nie ma sposobu na uzyskanie dosłownego `\', po którym następuje dopasowany przez funkcję tekst.

W 1992 standard POSIX usiłował rozwiązać ten problem. Standard stwierdza, że sub i gsub szukają po `\' albo `\' albo `&'. Jeżeli po `\' występuje któryś z tych znaków, to jest on wypisywany dosłownie. Interpretacja `\' i `&' staje się więc taka:

Wyglądało na to, że to rozwiązuje problem. Niestety, sposób wysławiania się standardu jest niecodzienny. W rezultacie, twierdzi on, że `\' wyłącza specjalne znaczenie każdego następującego po nim znaku, że, oprócz `\' i `&', takie specjalne znaczenie jest niezdefiniowane. Z takiego sformułowania wynikają dwa problemy.

  1. Odwrotne ukośniki w łańcuchu zastąpienie muszą być teraz podwojone, co psuje historyczne programy awk.
  2. Aby mieć pewność, że program awk jest przenośny, każdy znak w zastąpieniu musi być poprzedzony odwrotnym ukośnikiem(12)

Standard POSIX jest w trakcie poprawek.(13) Z powodu powyższych kłopotów, proponowany tekst dla skorygowanego standardu powraca do zasad ściślej odpowiadających pierwotnie istniejącej praktyce. Proponowane zasady mają przypadki specjalne, co umożliwia utworzenie `\' poprzedzającego dopasowany tekst.

W skrócie, na poziomie wykonania istnieją teraz trzy specjalne sekwencje znaków: `\\\&', `\\&' i `\&', podczas gdy, historycznie, była tu tylko jedna. Jednak, jak w przypadku historycznym, każdy `\', który nie jest częścią jednej z tych sekwencji specjalnych, nie ma znaczenia specjalnego i pojawia się na wyjściu dosłownie.

gawk 3.0 przestrzega tych, proponowanych przez POSIX, zasad dla sub i gsub. Czy zaproponowane reguły faktycznie zostaną skodyfikowane w standardzie w tym momencie nie wiadomo. Następne wydania gawk będą śledzić standard i wdrażać to, co określi jego ostateczna wersja. Niniejsza książka będzie także aktualizowana.

Reguły dla gensub są znacznie prostsze. Na poziomie wykonania, gdy awk dostrzeże `\', jeśli następnym znakiem jest cyfra, to w tworzonym wyjściu jest umieszczany tekst, który pasował do odpowiadającego jej umieszczonego w nawiasach podwyrażenia. W przeciwnym razie, bez względu na to, jaki znak występuje po `\', znak ten pojawi się w utworzonym tekście, a `\' nie wystąpi.

Z powodu złożoności przetwarzania na poziomie leksykalnym i wykonania oraz przypadków specjalnych dla sub i gsub, zalecamy stosowanie gawk i gensub w sytuacjach, gdy trzeba wykonać podstawienia.

12.4. Wbudowane funkcje wejścia/wyjścia

Poniższe funkcje związane są z wejściem/wyjściem (Input/Output (I/O). Parametry opcjonalne umieszczono w nawiasach kwadratowych ("[" i "]").

close(nazwapliku)
Zamyka plik nazwapliku, dla wejścia lub wyjścia. Alternatywnie argumentem może być polecenie powłoki, którego użyto do przekierowania do lub z potoku; wówczas zamykany jest potok. Zob. 6.8. Zamykanie potoków oraz plików wejściowych i wyjściowych, gdzie podano szczegóły.
fflush([nazwapliku])
Opróżnia buforowane wyjście skojarzone z nazwąpliku, będącą albo plikiem otwartym do zapisu, albo poleceniem powłoki przekierowującym wyjście do potoku. Wiele programów narzędziowych buforuje swoje wyjście. Przechowują one w pamięci informacje, jakie mają zostać zapisane na dysku czy terminalu, do czasu, aż będzie ich wystarczająco dużo, by warto było przesyłać dane na urządzenie wyjściowe. Jest to bardziej efektywne niż zapisywanie każdego kawałeczka danych od razu gdy jest gotowy. Czasami jednak konieczne jest wymuszenie na programie opróżnienia (wymiecenia, flush) tych buforów; to jest, zapisania informacji w miejscu ich przeznaczenia, nawet jeśli bufor nie jest zapełniony. Takie jest przeznaczenie funkcji fflush. gawk również buforuje swoje wyjście, a funkcji fflush można użyć do wymuszenia na nim, by opróżnił swoje bufory. fflush jest świeżym (1994) dodatkiem do wersji awk z Bell Labs research. Nie jest ona częścią standardu POSIX i nie będzie dostępna jeśli w wierszu poleceń podano opcję `--posix' (zob. 14.1. Opcje wiersza poleceń). gawk poszerza funkcję fflush na dwa sposoby. Pierwszym jest pozwolenie na brak argumentu. Opróżniany jest wówczas bufor standardowego wyjścia. Drugim jest zezwolenie na użycie jako argumentu łańcucha pustego (""). W tym przypadku opróżniane są bufory wszystkich otwartych plików wyjściowych i potoków. fflush zwraca zero jeśli pomyślnie opróżniono bufor, a nie-zero w przeciwnym razie.
system(polecenie)
Funkcja system umożliwia użytkownikowi wykonywanie poleceń systemu operacyjnego a następnie powrót do programu awk. Funkcja system wykonuje polecenie przekazane przez łańcuch polecenie. Zwraca, jako swoją wartość, kod zakończenia polecenia, które wykonała. Na przykład, jeśli w programie awk umieścimy poniższy fragment kodu:
END {
     system("date | mail -s 'koniec pracy awk' root")
}
do administratora systemu zostanie wysłana wiadomość w momencie gdy program awk zakończy przetwarzanie wejścia i rozpocznie przetwarzanie obsługi końca danych wejściowych. Zauważ, że często do wykonania zadania wystarczy przekierowanie print lub printf do potoku. Jeżeli potrzebujemy uruchomić wiele poleceń, efektywniejsze będzie po prostu wypisanie ich do potoku prowadzącego do powłoki:
while (więcej rzeczy do zrobienia)
    print polecenie | "/bin/sh"
close("/bin/sh")

Jeśli jednak program awk jest interaktywny, system przydaje się do uruchamiania dużych, niezależnych programów, jak powłoka czy edytor. Niektóre systemy operacyjne nie potrafią zaimplementować funkcji system. Jeżeli nie jest obsługiwane, system powoduje błąd krytyczny.

12.4.1. Buforowanie interaktywne a nieinteraktywne

Nawiasem mówiąc, zagadnienia buforowania mogą być nawet bardziej mylące w zależności od tego czy dany program jest interaktywny, tj. komunikuje się z użytkownikiem siedzącym przy klawiaturze.(14)

Programy interaktywne na ogół buforują wierszami swoje wyjście; zapisują na urządzenie wyjściowe każdy wiersz. Programy nieinteraktywne czekają aż zapełni się bufor, który może zawierać wiele wierszy wyjścia.

Oto przykład pokazujący tę różnicę.

$ awk '{ print $1 + $2 }'
1 1
-| 2
2 3
-| 5
Control-d

Każdy wiersz wyjścia jest wypisywany natychmiast. Porównajmy to zachowanie z poniższym.

$ awk '{ print $1 + $2 }' | cat
1 1
2 3
Control-d
-| 2
-| 5

Tutaj, nie są wypisywane żadne wyniki aż do chwili, gdy zostanie naciśnięte Control-d, gdyż całość wyjścia jest buforowana, i wysyłana potokiem do cat w jednym kroku.

12.4.2. Sterowanie buforowaniem wyjścia za pomocą system

Funkcja fflush umożliwia bezpośrednie sterowanie buforowaniem wyjścia poszczególnych plików i potoków. Jednak wywołanie nie jest przenośne do wielu innych implementacji awk. Alternatywną metodą opróżniania buforów wyjściowych jest wywołanie system z łańcuchem pustym jako argumentem:

system("")   # opróżnia wyjście

gawk traktuje takie wykorzystanie funkcji system jako przypadek specjalny, i jest wystarczająco sprytny, by nie uruchamiać powłoki (czy innego interpretera poleceń) z pustym poleceniem. Z tego powodu, w gawk idiom ten jest nie tylko przydatny, ale i efektywny. Mimo, że metoda ta powinna działać z innymi implementacjami awk, niekoniecznie uniknie wówczas zbędnego uruchamiania powłoki. (Możliwe, że inne implementacje opróżnią tylko bufor skojarzony ze standardowym wyjściem, a niekoniecznie całe buforowane wyjście.)

Jeśli pomyślimy o tym, czego spodziewa się programista, to zrozumiałe jest, że system powinno opróżniać całość oczekującego wyjścia. Poniższy program:

BEGIN {
     print "pierwszy print"
     system("echo system echo")
     print "drugi print"
}

musi wypisać

pierwszy print
system echo
drugi print

a nie

system echo
pierwszy print
drugi print

Jeśliby awk nie opróżniał buforów przed wywołaniem system, zobaczylibyśmy drugie (niepożądane) wyjście.

12.5. Funkcje obsługi znaczników czasu

Typowym zastosowaniem programów awk jest przetwarzanie plików dziennikowych zawierających znaczniki czasu, wskazujące kiedy zarejestrowano konkretny wpis dziennika. Wiele programów zapisuje znaczniki czasu w postaci zwracanej przez funkcję systemową time, będącej liczbą sekund od konkretnej daty (początku Epoki, Epoch). Na systemach POSIX-owych jest to liczba sekund od północy 1 stycznia 1970 czasu Greenwich (UTC).

Dla ułatwienia przetwarzania takich plików rejestracyjnych i tworzenia przydatnych zestawień, gawk udostępnia dwie funkcje przeznaczone do pracy ze znacznikami czasu. Obie są rozszerzeniami gawk; nie są wyszczególnione w standardzie POSIX, ani nie istnieją w żadnej innej znanej wersji awk.

Parametry opcjonalne umieszczono w nawiasach kwadratowych ("[" i "]").

systime()
Funkcja ta zwraca bieżący czas jako liczbę sekund od daty początku Epoki danego systemu. Na systemach POSIX-owych jest to liczba sekund od północy 1 stycznia 1970 UTC. Na innych systemach może to być odmienna liczba.
strftime([format [, timestamp]])
Funkcja ta zwraca łańcuch. Jest podobna do funkcji o tej samej nazwie występującej w ANSI C. Czas określony przez timestamp wykorzystywany jest do utworzenia łańcucha daty w oparciu o zawartość łańcucha format. timestamp ma ten sam format, co wartość zwracana przez funkcję systime. Jeżeli nie podano argumentu timestamp, gawk zastosuje jako znacznik czasu czas bieżący. Jeżeli nie podano argumentu format, strftime wykorzystuje "%a %b %d %H:%M:%S %Z %Y". Taki łańcuch formatu tworzy wyjście (prawie) równoważne temu, które daje narzędzie date. (Wersje gawk wcześniejsze niż 3.0 wymagają argumentu format.)

Funkcja systime umożliwia porównanie znacznika czasu z pliku dziennika z bieżącym czasem. W szczególności, łatwo jest stwierdzić, jak dawno temu zarejestrowano dany wpis. Pozwala też na tworzenie wpisów wykorzystujących format "liczby sekund od początku Epoki".

Funkcja strftime umożliwia łatwe przekształcanie znacznika czasu w dane czytelne dla człowieka. W swej istocie podobna jest do funkcji sprintf (zob. 12.3. Funkcje wbudowane działające na łańcuchach), w tym, że znaki nie tworzące specyfikacji formatu kopiuje do zwracanego łańcucha dosłownie, podczas gdy wartości daty i czasu zastępuje według specyfikacji z łańcucha format.

Standard ANSI C gwarantuje strftime obsługę następujących specyfikacji formatu daty:

%a
Skrócona narodowa (locale) nazwa dnia tygodnia.
%A
Pełna narodowa nazwa dnia tygodnia.
%b
Skrócona narodowa nazwa miesiąca.
%B
Pełna narodowa nazwa miesiąca.
%c
"Właściwa" narodowa reprezentacja daty i czasu.
%d
Dzień miesiąca jako liczba dziesiętna (01--31).
%H
Godzina (zegar 24-godzinny) jako liczba dziesiętna (00--23).
%I
Godzina (zegar 12-godzinny) jako liczba dziesiętna (01--12).
%j
Dzień roku jako liczba dziesiętna (001--366).
%m
Miesiąc jako liczba dziesiętna (01--12).
%M
Minuta jako liczba dziesiętna (00--59).
%p
Narodowy równoważnik oznaczeń AM/PM związanych z zegarem 12-godzinnym.
%S
Sekunda jako liczba dziesiętna (00--60).(15)
%U
Numer tygodnia w roku (pierwsza niedziela jako pierwszy dzień pierwszego tygodnia) jako liczba dziesiętna (00--53).
%w
Dzień tygodnia jako liczba dziesiętna (0--6). Niedziela to dzień zero.
%W
Numer tygodnia w roku (pierwszy poniedziałek jako pierwszy dzień pierwszego tygodnia) jako liczba dziesiętna (00--53).
%x
"Właściwa" narodowa reprezentacja daty.
%X
"Właściwa" narodowa reprezentacja czasu.
%y
Rok bez stulecia jako liczba dziesiętna (00--99).
%Y
Rok ze stuleciem jako liczba dziesiętna (np., 1995).
%Z
Nazwa strefy czasowej lub jej skrót, albo brak znaków jeśli nie można ustalić żadnej strefy.
%%
Dosłowny `%'.

Jeżeli specyfikator konwersji nie jest jednym z powyższych, zachowanie jest niezdefiniowane.(16)

Nieformalnie, locale [tłum.: oddawane w przekładzie jako ustawienia regionalne/narodowe] określa miejsce geograficzne, w którym mamy zamiar uruchamiać program. Na przykład, popularną metodą skracania daty 4 września 1991 w Stanach Zjednoczonych jest "9/4/91". jednak W wielu krajach Europy zapisanoby ją "4.9.91". Zatem, specyfikacja `%x' w ustawieniach narodowych "US" może dawać `9/4/91', podczas gdy przy ustawieniu "EUROPE" może dać `4.9.91'. Standard ANSI C definiuje jako domyślne ustawienie regionalne "C", będące typowym środowiskiem jakiego używa większość programistów C.

Dla systemów, które nie są jeszcze w pełni zgodne z ANSI, razem z gawk dostarczana jest na zasadach public-domain strftime w wersji C. Jeżeli do kompilacji gawk (zob. B. Instalowanie gawk) zostanie użyta ta właśnie wersja, to dostępne są następujące dodatkowe specyfikatory formatu:

%D
Równoważne podaniu `%m/%d/%y'.
%e
Dzień miesiąca, uzupełniony spacją jeśli jest jednocyfrowy.
%h
Równoważne `%b', powyżej.
%n
Znak nowej linii (ASCII LF).
%r
Równoważne podaniu `%I:%M:%S %p'.
%R
Równoważne podaniu `%H:%M'.
%T
Równoważne podaniu `%H:%M:%S'.
%t
Znak tabulacji.
%k
Godzina (zegar 24-godzinny) jako liczba dziesiętna (0-23). Liczby jednocyfrowe uzupełniane są spacją.
%l
Godzina (zegar 12-godzinny) jako liczba dziesiętna (1-12). Liczby jednocyfrowe uzupełniane są spacją.
%C
Stulecie, jako liczba między 00 a 99.
%u
Dzień tygodnia jako liczba dziesiętna [1 (poniedziałek)--7].
%V
Numer tygodnia w roku (pierwszy poniedziałek jako pierwszy dzień tygodnia numer jeden) jako liczba dziesiętna (01--53). Metoda wyznaczania numeru tygodnia określona jest przez ISO 8601 (innymi słowy: jeśli tydzień zawierający dzień 1 stycznia ma cztery lub więcej dni należące do nowego roku, to jest on tygodniem numer jeden, w przeciwnym razie jest to tydzień numer 53 roku poprzedniego a tygodniem numer jeden jest następny tydzień).
%G
Rok, ze stuleciem, numeru tygodnia wg ISO, jako liczba dziesiętna. Na przykład, 1 stycznia 1993 jest w tygodniu 53 roku 1992. Zatem, rokiem jego numeru ISO tygodnia jest 1992, mimo że jego rokiem jest 1993. Podobnie, 31 grudnia 1973 jest w tygodniu 1 roku 1974. Zatem, rokiem jego numeru ISO tygodnia jest 1974, mimo że jego rokiem jest 1973.
%g
Rok, bez stulecia, numeru ISO tygodnia, jako liczba dziesiętna (00--99).
%Ec %EC %Ex %Ey %EY %Od %Oe %OH %OI
%Om %OM %OS %Ou %OU %OV %Ow %OW %Oy
Są to "alternatywne reprezentacje" dla specyfikacji używających tylko drugiej litery (`%c', `%C', i tak dalej). Są rozpoznawane, ale używana jest ich normalna reprezentacja.(17) (Funkcje te zapewniają zgodność z POSIX-owym narzędziem date.)
%v
Data w formacie VMS (np., 20-JUN-1991).
%z
Przesunięcie strefy czasowej w formacie +HHHH (np. format niezbędny do utworzenia nagłówków daty RFC-822/RFC-1036).

Poniższy przykład jest realizacją POSIX-owego narzędzia date wykonaną w awk. Normalnie date wypisuje bieżącą datę i czas w dobrze znanym formacie. Jeśli jednak podamy mu argument zaczynający się od `+', to date skopiuje znaki nie tworzące specyfikatora formatu na standardowe wyjście. Bieżący czas natomiast zinterpretuje zgodnie ze specyfikatorami formatu zawartymi w podanym łańcuchu. Na przykład:

$ date '+Dziś jest %A, %d %B %Y.'
-| Dziś jest czwartek, 11 lipiec 1991.

[tłum.: Cóż, powinno być "11 lipca", ale wartości locale nie uwzględniają subtelności gramatyki]

Oto wykonana w gawk wersja narzędzia date. Posiada otoczkę ("wrapper") powłoki, do obsługi opcji `-u', która wymaga by date zostało uruchomione tak, jakby strefę czasową ustawiono na UTC.

#! /bin/sh
#
# date --- przybliżenie polecenia 'date' z P1003.2

case $1 in
-u)  TZ=GMT0     # użyj UTC
     export TZ
     shift ;;
esac

gawk 'BEGIN  {
    format = "%a %b %d %H:%M:%S %Z %Y"
    exitval = 0

    if (ARGC > 2)
        exitval = 1
    else if (ARGC == 2) {
        format = ARGV[1]
        if (format ~ /^\+/)
            format = substr(format, 2)   # usuń początkowy +
    }
    print strftime(format)
    exit exitval
}' "$@"

 


Przejdź do pierwszej, poprzedniej, następnej, ostatniej sekcji, spisu treści.

 
Linki sponsorowane

W celu realizacji usług i funkcji na witrynach internetowych ZUI "ELPRO" stosujemy pliki cookies. Korzystanie z witryny bez zmiany ustawień dotyczących plików cookies oznacza, że będą one zapisywane w urządzeniu wyświetlającym stronę internetową. Więcej szczegółów w Polityce plików cookies.

Akceptuję pliki cookies z tej witryny.