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.)
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.
Oto pełna lista funkcji wbudowanych działających na liczbach. Parametry opcjonalne umieszczono w nawiasach kwadratowych ("[" i "]").
int(x)
int(3) jest trzy, int(3.9) jest trzy,
int(-3.9) jest -3, a int(-3) jest także -3.
sqrt(x)
sqrt(4) wynosi dwa.
exp(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)
sin(x)
cos(x)
atan2(y, x)
y / x w radianach.
rand()
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])
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.
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)
$ 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])
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)
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])
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,...)
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])
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])
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ść])
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)
tolower("MiEsZaNe 123") zwraca
"mieszane 123".
toupper(łańcuch)
tolower("MiEsZaNe 123") zwraca
"MIESZANE 123".
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.
awk.
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.
Poniższe funkcje związane są z wejściem/wyjściem (Input/Output (I/O). Parametry opcjonalne umieszczono w nawiasach kwadratowych ("[" i "]").
close(nazwapliku)
fflush([nazwapliku])
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)
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.
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.
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.
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()
strftime([format [, timestamp]])
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
%A
%b
%B
%c
%d
%H
%I
%j
%m
%M
%p
%S
%U
%w
%W
%x
%X
%y
%Y
%Z
%%
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
%e
%h
%n
%r
%R
%T
%t
%k
%l
%C
%u
%V
%G
%g
%Ec %EC %Ex %Ey %EY %Od %Oe %OH %OI
%Om %OM %OS %Ou %OU %OV %Ow %OW %Oy
date.)
%v
%z
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.