!Štítky: {program}{syntaxe}{zpracování textu}{programování} !OblíbenáZaklínadla: !ÚzkýRežim: zap
GNU AWK je skriptovací nástroj pro řádkově orientované zpracování textových souborů. Nabízí podstatně více možností než „sed“, ale ve srovnání s Perlem zůstává velmi omezený (např. jeho jedinou datovou strukturou je asociativní pole), což ho činí velmi vhodným pro začátečníky, ale nedostačujícím pro komplikovanější projekty. Syntaxe AWK je (zvlášť ve srovnání s Perlem) elegantní a umírněná.
Skript AWK se skládá ze sekvence takzvaných „vzorků“ (podmínek) a k nim příslušejících bloků příkazů. AWK rozdělí vstupní soubory po řádcích (záznamech), každý záznam rozdělí na sloupce a pro každý záznam postupně prochází celý skript a testuje jeden vzorek po druhém. Když vzorek vyhoví, příslušný blok příkazů se vykoná, jinak se přeskočí. Kromě toho AWK spouští i několik dalších (zvláštních) iterací, kdy se vykonají bloky označené určitým klíčovým slovem (např. BEGIN).
Vzorek nebo blok příkazů je dovoleno vynechat; vynecháme-li vzorek, blok příkazů se vykoná pro každou řádku (ale ne ve speciálních iteracích); vynecháme-li blok příkazů, automaticky se doplní „{print $0}“.
Nejčastějším tvarem vzorku je podmínka tvořená pouze literálem regulárního výrazu (např. „/^a/“), taková podmínka se (nejen v tomto kontextu) automaticky rozšíří na výraz „($0 ~ /^a/)“, tedy porovnání načteného řádku s uvedeným regulárním výrazem.
Pozor! V AWK se všechny druhy indexů a číslování číslují vždy od jedničky, nikdy od nuly!
- Vzorek (pattern) je podmínka, která určuje, zda se daný blok příkazů má v dané iteraci skriptu provést. Podmínkou může být obecný výraz nebo jedno z klíčových slov, která identifikují speciální iterace.
- Záznam (record) je typicky řádka vstupního souboru. Způsob ukončení záznamu ve vstupních souborech určuje zvláštní proměnná „RS“ (record separator), jejíž výchozí hodnota je "\n".
- Záznam se po načtení rozdělí do sloupců (fields). Způsob oddělení záznamů se nastavuje zvláštní proměnnou „FS“ (field separator), jejíž výchozí hodnotou je mezera, která má zvláštní význam odpovídající regulárnímu výrazu „\s+“.
- Regulární výraz může být ve skriptu AWK zadán buď jako literál do lomítek, např. „/^a/“, nebo jako dynamický regulární výraz, což je jakýkoliv řetězec či řetězcový výraz zadaný v místě, kde se očekává regulární výraz. Tyto dva způsoby zadání jsou většinou víceméně rovnocenné, ale liší se požadavky na odzvláštnění (v literálu musíte odzvláštnit všechny výskyty znaku „/“, a to i uvnitř hranatých závorek) a tím, že dynamický regularní výraz se nikdy automaticky nedoplní o prefix „$0␣~“, zatímco literál to dělá skoro vždy.
- Jmenný prostor (namespace) je oblast platnosti pro globální identifikátory. Výchozí je jmenný prostor „awk“, také zvaný globální jmenný prostor. Jmenné prostory neplatí pro identifikátory tvořené pouze velkými písmeny anglické abecedy (např. „ABC“ nebo „PROMENNA“) a pro klíčová slova (např. „sin“ nebo „if“). Rovněž se nevztahují na lokální identifikátory (názvy parametrů funkcí).
!ÚzkýRežim: vyp
# podmíněné vykonávání (obecně)
{podmínka} [{{blok příkazů}}]
# podmíněné vykonávání (příklady)
PROMENNA == 1 && /^X/ {ZAJIMAVY_RADEK = $0}
$1 ~ /ABC/ {print $2}
priznak {next}
$0 != "xyz" {print "ne-XYZ:";print $0;}
!/^#/
length($0) > 5 || /^%/ {print "Řádka " FNR " podmínku splnila."}
# vykonat blok příkazů pro každou řádku
{{blok příkazů}}
# před otevřením prvního souboru
BEGIN {{blok příkazů}}
# před definitivním ukončením skriptu
// Pozor: zvláštní průchod END se vykoná i tehdy, je-li skript ukončován příkazem „exit“!
END {{blok příkazů}}
# po otevření souboru (ale před načtením prvního řádku)
BEGINFILE {{blok příkazů}}
# po zpracování poslední řádky souboru
// Pozor: zvláštní průchod ENDFILE se nevykoná, pokud je zpracování souboru předčasně ukončeno, např. příkazem „nextfile“ či „exit“.
ENDFILE {{blok příkazů}}
# řádky „od“ až „do“ včetně hranic
{podmínka od},{podmínka do} [{{blok příkazů}}]
# řádky „od“ až „do“ kromě hranic
{if (!{pomocná_proměnná}) {{pomocná_proměnná} = {podmínka od}} else if ({podmínka do}) {{pomocná_proměnná} = 0} else {{blok příkazů}}}
# přečíst hodnotu proměnné (z aktivního jmenného prostoru/z globálního/z konkrétního)
{název_proměnné}
awk::{název_proměnné}
{jmenny_prostor}::{název_proměnné}
# přiřadit hodnotu proměnné
[{jmenny_prostor}::]{název_proměnné} = {hodnota}
# získat hodnotu proměnné prostředí (obecně/příklad)
ENVIRON[{řetězec-s-názvem-proměnné}]
ENVIRON["PATH"]
# přiřadit hodnotu proměnné prostředí (obecně/příklad)
ENVIRON[{řetězec-s-názvem-proměnné}] = {řetězec}
ENVIRON["PATH"] = "/bin:/usr/bin";
# nepřímý přístup k proměnné (příklad)
[{jmenny_prostor}::]{nazev_promenne} = "{hodnota}";
UKAZATEL = "[{jmenny_prostor}::]{nazev_promenne}";
print SYMTAB[UKAZATEL];
SYMTAB[UKAZATEL] = "nova hodnota"
# přečíst hodnotu prvku pole
[{jmenny_prostor}::]{pole}[{index}]
# přiřadit hodnotu prvku pole
[{jmenny_prostor}::]{pole}[{index}] = {hodnota}
# existuje prvek pole?
{index} in [{jmenny_prostor}::]{pole}
# smazat z pole jeden prvek/všechny prvky
delete [{jmenny_prostor}::]{pole}[{index}];
delete [{jmenny_prostor}::]{pole};
# počet prvků
length([{jmenny_prostor}::]{pole})
# zkopírovat celé pole
delete {cílové_pole};
for ({pomocná_proměnná} in {pole}) {{cílové_pole}[{pomocná_proměnná}] = {pole}[{pomocná_proměnná}]}
# je proměnná pole?
// Tato funkce je nejužitečnější u parametrů funkcí; díky ní může funkce ověřit, že jí v určitém parametru bylo předáno pole, nebo naopak skalární hodnota.
isarray({proměnná})
Poznámka k řazení asociativních polí: výsledkem řazení asociativního pole je vždy pole indexované celými čísly od 1 do návratové hodnoty řadicí funkce.
V následujících příkazech: „typ“ může být „num“ (řadit jako čísla) nebo „str“ (řadit jako řetězce, ale podle pozice znaků ve znakové sadě Unicode); „pořadí“ může být „asc“ (vzestupně) nebo „desc“ (sestupně).
# seřadit hodnoty podle klíčů/podle hodnot/uživatelskou funkcí
// Při řazení uživatelskou funkcí musíte zadat funkci, která přijme parametry „klíč-L“, „hodnota-L“, „klíč-R“ a „hodnota-R“ v tomto pořadí a vrátí záporné číslo, je-li L-strana menší než R-strana; kladné číslo, je-li větší; a nulu, jsou-li si obě strany rovny.
asort({pole}, {cílové_pole}, "@ind_{typ}_{pořadí}")
asort({pole}, {cílové_pole}, "@val_{typ}_{pořadí}")
asort({pole}, {cílové_pole}, "{jméno_funkce}")
# seřadit klíče podle klíčů/podle hodnot/uživatelskou funkcí
asorti({pole}, {cílové_pole}, "@ind_{typ}_{pořadí}")
asorti({pole}, {cílové_pole}, "@val_{typ}_{pořadí}")
asorti({pole}, {cílové_pole}, "{jméno_funkce}")
# příklad řazení
pocet_jmen = asort(prostor::jmena, prostor::serazena_jmena, "@val_str_asc");
# podmínka if
if ({podmínka}) {příkaz-nebo-blok} [else {příkaz-nebo-blok}]
# cyklus while
while ({podmínka}) {příkaz-nebo-blok}
# cyklus for
for ([{inicializace}]; [{podmínka}]; [{iterace}]) {příkaz-nebo-blok}
# cyklus foreach
// Poznámka: cyklus přiřazuje proměnné hodnoty INDEXŮ daného pole v LIBOVOLNÉM pořadí! Pro přístup k hodnotám v poli je musíte indexovat.
for ({proměnná} in {pole}) {příkaz-nebo-blok}
# přepínač switch
// Přepínač switch pracuje v gawk stejně jako v jazyce C, ovšem pracuje s řetězci místo celých čísel.
switch ({skalární-výraz}) {
[[case {konstantní-hodnota}:]... [default:] {příkazy}]...
}
# cyklus do-while (alternativy)
do { {příkazy} } while ({podmínka});
do {příkaz}; while ({podmínka});
# opustit nejvnitřnější cyklus (for, while, do) nebo přepínač (switch)
break;
# skočit na podmínku nejvnitřnějšího cyklu (for, while, do)
continue;
# přejít na zpracování dalšího řádku
next;
# přejít na zpracování dalšího souboru
// Poznámka: tento příkaz přeskočí odpovídající průchod ENDFILE!
nextfile;
# přejít na průchod END („ukončit skript“)
exit [{návratová-hodnota-programu}];
# vypsat chybovou zprávu a skončit s chybou
print {chybová-zpráva-řetězec} > "/dev/stderr"
exit 1;
Poznámka: Dělení záznamů na sloupce určují proměnné FS, FPAT a FIELDWIDTHS.
# dělit sloupce libovolnou sekvencí bílých znaků
[FPAT = FIELDWIDTHS = "";] FS = "␣";
# dělit sloupce tabulátorem
[FPAT = FIELDWIDTHS = "";] FS = "\t";
# dělit sloupce regulárním výrazem
[FPAT = FIELDWIDTHS = "";] FS = {"regulární výraz"};
# zapnout/vypnout režim sloupců pevné šířky
// Je-li uvedena hodnota „kolik-přeskočit“, před načtením sloupce se přeskočí daný počet znaků. Výhradně u posledního sloupce můžete místo počtu znaků zadat „*“; v takovém případě se do daného sloupce uloží všechny zbylé znaky.
FIELDWIDTHS = "[[{kolik-přeskočit}:]{šířka-dalšího-sloupce}␣]...[{kolik-přeskočit}:]{šířka-posl-sloupce}"
FIELDWIDTHS = ""
# režim sloupců pevné šířky (příklady)
FIELDWIDTHS = "5 2 7" ⊨ $1 = „12345“ $2 = „67“ $3 = „89ABCDE“
FIELDWIDTHS = "1:3 2:2 1:*" ⊨ $1 = „234“ $2 = „78“ $3 = „ABCDEF“
# vypnout dělení na sloupce
FS = RS; FPAT = FIELDWIDTHS = "";
# každý znak jako samostatný sloupec
FS = "";
# dělit sloupce výhradně mezerou
FS = "[␣]";
# sloupce ze shod s regulárním výrazem (zapnout/vypnout)
FPAT = {"neprázdný regulární výraz"};
FPAT = "";
# načtená řádka (bez oddělovače záznamu/s oddělovačem záznamu)
$0
$0 RT
# číslo řádky v souboru/celkově
// Do obou uvedených proměnných můžete také přiřadit novou hodnotu, a změnit tak číslování řádku pro zbytek souboru (resp. veškerého vstupu)
FNR
NR
# sloupec načtené řádky (obecně/příklady...)
${číselný-výraz}
$2
$12
$(NF - 1)
# název/index právě načítaného souboru
// Nelze použít v blocích BEGIN a END.
FILENAME
ARGIND
# počet sloupců v načteném řádku
// Počet sloupců lze i nastavit.
NF
# výstupní oddělovač sloupců/záznamů příkazu print
// Výchozí hodnota je „ “ (mezera); může být přednastaven také parametrem příkazové řádky „-F“.
OFS = "[{řetězec}]"
# vstupní oddělovač záznamu (jeden znak/regulární výraz)
// Výchozí hodnotou je konec řádku „\n“.
RS = "{znak}"
RS = "{regulární výraz}"
# hodnota posledního sloupce načteného řádku
$NF
# počet souborů, které mají být (podle příkazové řádky) načteny jako vstupní
// Poznámka: vrací-li 0, stejně má být čten standardní vstup.
ARGC - 1
# N-tý (od 1) vstupní soubor, jak je zadán na příkazovém řádku
ARGV[{N}]
# komentář
// Znak # není interpretován jako začátek komentáře uvnitř řetězců ani literálů regulárních výrazů.
#{libovolný obsah až do konce řádky}
# příkaz print
// Příkaz print zapíše na výstup svoje parametry, oddělené obsahem proměnné „OFS“ (výchozí hodnotou je "␣"), a za poslední parametr přidá navíc hodnotu proměnné „ORS“ (výchozí hodnota je "\n").
print {výraz}[, {další-výraz}]... [{přesměrování-výstupu}];
# funkce printf
*// Funkce printf() funguje stejně jako v jazyce C, až na to, že parametr %c dokáže vypisovat UTF-8 znaky v rozsahu 0 až cca 55000 (ale např. místo znaku č. 55339 vypíše „+“). *
printf({formátovací-řetězec}[,{výraz}]...) [{přesměrování-výstupu}];
# uzavřít soubor otevřený ke čtení či zápisu
// Tato varianta funkce „close()“ vrací 0 v případě úspěchu, jiná hodnota značí chybu.
close({"řetězec/reprezentující/soubor"})
# uzavřít čtení výstupu příkazu
close({"řetězec reprezentující příkaz"})
# uzavřít zápis na vstup příkazu
// Funkce „close()“ počká na skončení příkazu a vrátí jeho návratovou hodnotu.
close({"řetězec reprezentující příkaz"})
# vyprázdnit vyrovnávací paměť zápisu (soubor/standardní výstup)
fflush({"řetězec/reprezentující/soubor"})
fflush("/dev/stdout")
# vykonat příkaz interpretem „/bin/sh“ a vrátit jeho návratovou hodnotu
system({řetězec})
Poznámky k přesměrování výstupu: Prvním zápisem do souboru, který ještě není otevřen, se tento soubor automaticky otevře pro zápis a zůstane otevřený pro další zápisy, dokud ho neuzavřete funkcí close() nebo do konce programu. Analogicky platí, že prvním zápisem na vstup dosud nespuštěného příkazu se tento příkaz spustí a další zápisy směřují na vstup téže instance příkazu, dokud spojení neuzavřete funkcí „close()“.
Pokud soubor existuje, při otevření se jeho obsah smaže; pokud chcete přidávat na konec souboru, použijte místo operátoru „>“ operátor „>>“ (princip je stejný jako v bashi).
# výstup do souboru (příklad)(print/printf)
print "A:", A > "../seznam.txt";
printf("%s: %d\n", I, POLE[I]) > "hodnoty.txt";
# poslat na (standardní) vstup jiného příkazu (print/printf)
// Odkazovaný příkaz se spustí v interpretu /bin/sh a svoje vstupy a výstupy zdědí od instance GNU awk, kterou byl spuštěn, pokud je výslovně nepřesměrujete.
print {parametr}[, {další parametr}]... | {"řetězec s příkazem"} ;
printf({formátovací-řetězec}[,{parametr}]...) | {"řetězec s příkazem"} ;
# zapsat na vstup jiného příkazu (příklad)
print I, S, "." | "sort -n";
# zapsat na standardní chybový výstup
print {parametry} > "/dev/stderr";
printf({parametry}) > "/dev/stderr";
# zapsat do souboru jeden znak (přepsat/připojit)
printf("%s", "{znak}") > {"řetězec/s/cestou/souboru"})
Poznámky k přesměrování vstupu: Prvním čtením ze souboru, který ještě není otevřen, se tento soubor automaticky otevře pro čtení a zůstané otevřený pro čtení dalších řádků, dokud ho neuzavřete funkcí close(). Analogicky platí, že čtení z příkazu, který ještě nebyl spuštěn, ho spustí a další čtení čtou z výstupu téže instance, dokud spojení neuzavřete funkcí „close()“.
# přečíst řádku ze souboru
[if (]getline [{PROMĚNNÁ}] < {"řetězec/s/cestou/souboru"}[) {tělo příkazu if}]
# přečíst jeden znak ze souboru
// Poznámka: po použití této konstrukce pravděpodobně budete muset obnovit hodnoty proměnných RS, $0, RT, NR a FNR.
RS = "(.)";
if (getline < {"řetězec/s/cestou/souboru"}) {{proměnná} = RT}
# přečíst celý soubor do zadané proměnné
normalni_RS = RS; RS = "^$"; {PROMĚNNÁ} = "";
getline {PROMĚNNÁ} < {"řetězec/s/cestou/souboru"};
RS = normalni_RS;
[close({"řetězec/s/cestou/souboru"});]
# načíst řádku z výstupu příkazu
[if (]{"příkaz"} | getline [{PROMĚNNÁ}]) {tělo příkazu if}
# zapsat na standardní vstup koprocesu (alternativy)
{příkaz print} |& {koproces};
# přečíst řádek z výstupu koprocesu
[if (]{koproces} |& getline [{PROMĚNNÁ}][) {tělo příkazu if}]
# uzavřít jen vstup koprocesu
// Po uzavření vstupu koprocesu můžete pouze číst z jeho výstupu. Pokus o další zápis před úplným ukončením koprocesu je fatální chyba.
close({koproces}, "to");
# vyčkat na ukončení koprocesu
// Funkce close() v tomto případě vrátí návratovou hodnotu koprocesu vynásobenou 256.
close({koproces})
# PID procesu gawk/rodičovského procesu
PROCINFO["pid"] ⊨ 7034
PROCINFO["ppid"] ⊨ 4052
# verze GNU awk
PROCINFO["version"] ⊨ 5.0.1
# definovat funkci (volitelně s lokálními proměnnými)
function {název funkce}([{první-parametr}[,{další-parametry}]...][,␣␣␣{lokální-proměnná}[, {další-lokální-proměnná}]...]) {blok příkazů}
# volání funkce
{název_funkce}([{parametr}[, {další parametr}]])
# nepřímé volání funkce
@{proměnná}([{parametr}[, {další parametr}]])
Poznámka k řetězci náhrady: V tomto řetězci je nutno odzvláštnit znaky „\“ a „&“, protože mají speciální význam: Funkce sub(), gensub() a gsub() za neodzvláštněný znak „&“ dosadí text shody s nahrazovaným regulárním výrazem. Funkce „gensub()“ navíc za značky „\1“ až „\9“ (do řetězce nutno zadávat jako "\\1" atd.) dosadí text číslovaného záchytu (podřetězec odpovídající seskupení v regulárním výrazu).
# vyhovuje/nevyhovuje regulárnímu výrazu?
{řetězec} ~ {regulární-výraz}
{řetězec} !~ {regulární-výraz}
# nahradit první výskyt/N-tý výskyt/všechny výskyty regulárního výrazu v proměnné
sub({regulární výraz}, {řetězec-náhrady}, {proměnná}) ⊨ počet náhrad (0, nebo 1)
{proměnná} = gensub({regulární výraz}, {řetězec-náhrady}, {N}, {proměnná}) ⊨ řetězec po náhradě (nezměněný, pokud k náhradě nedošlo)
gsub({regulární výraz}, {řetězec-náhrady}, {proměnná}) ⊨ počet náhrad
# nahradit první výskyt/N-tý výskyt/všechny shody v $0
sub({regulární výraz}, {řetězec-náhrady}) ⊨ počet náhrad (0, nebo 1)
$0 = gensub({regulární výraz}, {řetězec-náhrady}, {N}) ⊨ řetězec po náhradě (nezměněný, pokud k náhradě nedošlo)
gsub({regulární výraz}, {řetězec-náhrady}) ⊨ počet náhrad
# nahradit první výskyt/N-tý výskyt/všechny výskyty regulárního výrazu v řetězci
gensub({regulární výraz}, {řetězec-náhrady}, 1, {řetězec})
gensub({regulární výraz}, {řetězec-náhrady}, {N}, {řetězec})
gensub({regulární výraz}, {řetězec-náhrady}, "g", {řetězec})
# najít a vypsat první shodu s regulárním výrazem
// Nebyla-li shoda s regulárním výrazem nalezena, funkce match() vrací 0; jinak nastaví proměnné RSTART a RLENGTH na pozici a délku nalezeného podřetězce a vrátí hodnotu RSTART. Vždy vybírá nejlevější a nejdelší shodu.
if (match({řetězec}, {regulární-výraz})) {
print substr({řetězec}, RSTART, RLENGTH);
}
# najít všechny shody s regulárním výrazem a sestavit z nich číslované pole
// Funkce „patsplit()“ vyhledá všechny shody řetězce s regulárním výrazem a vrátí jejich počet (N). Předané pole pak smaže a naplní těmito shodami. Zadáte-li i pole pro oddělovače, pak ho tato funkce vyplní podřetězci, které zbyly mezi jednotlivými shodami, přičemž řetězec před první shodou bude umístěn na indexu 0 a řetězec za poslední shodou na indexu N.
patsplit({řetězec}, {pole}, {regulární-výraz} [,{pole-pro-oddělovače}]) ⊨ počet shod (např. „3“)
# rozdělit řetězec do pole, oddělovač je definovaný regulárním výrazem
split({řetězec}, {pole}, {regulární-výraz}[,{pole_pro_oddělovače}])
# příklad: v řetězci „a=15:b=79“ v proměnné „x“ vyměnit čísla
x = gensub(/^a=([0-9]+):b=([0-9]+)$/, "a=\\2:b=\\1", 1, x); ⊨ a=79:b=15
# spojit dva řetězce (konkatenace)
{jeden-řetězec} {druhý-řetězec}
# získat podřetězec podle pozice
substr({řetězec}, {počáteční-pozice}[, {maximální-délka}])
# najít pozici prvního výskytu podřetězce
// Vrátí 0, nebyl-li podřetězec nalezen; jinak vrátí pozici podřetězce (číslovanou od 1).
index({řetězec}, {hledaný-podřetězec})
# zjistit délku řetězce
length({řetězec})
# rozdělit řetězec do pole (podle FS/určitým znakem)
split({řetězec}, {pole})
split({řetězec}, {pole}, "{znak}"[,{pole_pro_oddělovače}])
# naformátovat řetězec (funkce sprintf známá z jazyka C)
sprintf({formátovací-řetězec}[, {parametr}]...)
# převod na malá písmena/velká písmena
// V gawk tyto funkce rozpoznávají znaky národní abecedy (pravděpodobně podle „locale“), v mawk účinkují pouze na ASCII znaky.
tolower({řetězec})
toupper({řetězec})
# sekvence více výskytů téhož znaku v řetězci nahradit jeho jedním výskytem
?
# desítkové číslo na hexadecimální/hexadecimální na desítkové
sprintf("[0x]%x", {číslo})
strtonum("0x{hex-číslo}")
# desítkové číslo na osmičkové/osmičkové na desítkové
sprintf("[0]%o", {číslo})
strtonum("0{osmičkové-číslo}")
# absolutní hodnota
{x} >= 0 ? {x} : -{x}
# zaokrouhlit desetinné číslo na nejbližší celé číslo/k nule/jen oříznout desetinnou část
{číslo} >= 0 ? int({číslo} + 0.4999999) : int({číslo} - 0.4999999)
{číslo} >= 0 ? int({číslo}) : -int(-{číslo})
int({hodnota})
# vygenerovat pseudonáhodné celé číslo 0 <= y < maximum
// Vhodné jen pro maxima do 16 777 215. Pro vyšší maximum bude množina vrácených hodnot pravidelně přerušovaná, což pro některé druhy využití nemusí vadit. Správným řešením je bitová kombinace více volání funkce „rand()“.
int(rand() * {maximum})
# vygenerovat pseudonáhodné celé číslo 0 <= y < 4 294 967 296
65356 * int(65536 * rand()) + int(65536 * rand())
# nastavit počáteční „semínko“ generátoru pseudonáhodných čísel (na hodnotu/podle času)
srand({hodnota-semínka})
srand()
# vygenerovat pseudonáhodné desetinné číslo 0 <= y < 1
rand()
# druhá odmocnina/druhá mocnina
sqrt({x})
{x}^2
# arcus tangens y / x
atan2({y}, {x})
# sinus/kosinus/tangens/contangens
sin({x})
cos({x})
sin({x}) / cos({x})
cos({x}) / sin({x})
# přirozený logaritmus / e na x-tou
log({x})
exp({x})
# aktuální čas (jako časová známka Unixu)
systime()
# zformátovat čas
// Bez parametru „1“ vypíše lokální čas, s ním vypíše UTC. Pro „formát“ – viz kapitolu „Datum, čas a kalendář“.
strftime({formát},{časová-známka-Unixu}[, 1])
# přepnout se do jmenného prostoru/do globálního jmenného prostoru
// Direktiva „@namespace“ musí být použita na úrovni souboru (tj. mimo jakýkoliv blok) a platí do nejbližší další direktivy @namespace nebo do konce souboru. Účinkuje pouze v souboru, ve kterém je uvedena. Je-li vložen nebo připojen kód z jiného souboru (např. direktivou „@include“), ten má svoje vlastní řízení jmenného prostoru a začíná vždy globálním jmenným prostorem „awk“.
@namespace "{jmenný_prostor}"
@namespace "awk"
# vložit kód z jiného zdrojového souboru
// Aktuální jmenný prostor nemá vliv na interpretaci identifkátorů ve vkládaném souboru! Na jeho počátku bude platit jmenný prostor „awk“, dokud nebude změněn direktivou „@namespace“.
@include "{cesta/k/souboru.awk}"
# implementovat načítání řádek rozdělených znakem \ před znakem konce řádku (tento kód vložit na začátek skriptu)
{proměnná} != "" {$0 = {proměnná}; {proměnná} = "";}
/(^|[^\\])(\\\\)*\\$/ {{proměnná} = substr($0, 1, length($0) - 1); next;}
*# *
gawk {parametry-gawk} [[--] {vstupní-soubory}]...
gawk {parametry-gawk} [[--] {parametry-skriptu}]...
gawk {parametry-kromě-e-f-i} {program} [[--] {vstupní-soubory}]...
gawk --version
!parametry:
- -F {hodnota} :: Přednastaví hodnotu proměnné FS (vstupního oddělovače sloupců).
- -v {proměnná}={hodnota} :: Přednastaví hodnotu určité proměnné.
- -f {soubor} :: Načte kód ke spuštění z daného souboru.
- -e {kód} :: Vezme uvedený kód ke spuštění.
- -b :: Vstup a výstup realizuje po bajtech, ne v UTF-8.
- --dump-variables={soubor} :: Po skončení zapsat hodnoty všech proměnných do daného souboru.
- --profile={soubor} :: Shromáždí „profilovací“ data a po skončení programu je zapíše do uvedeného souboru. (Nezkoušeno.)
- --sandbox :: Vypne všechny funkce, které by mohl skript použít k přístupu k jiným souborům než těm, které mu byly předány na příkazovém řádku; to zahrnuje např. funkci „system()“, přesměrování výstupů apod.
Poznámka: Parametry -f a -e můžete kombinovat a zadávat opakovaně. Každý další takový parametr přidá takový kód k již načtenému, takže se nakonec všechny spojí do jednoho programu.
*# *
sudo apt-get install gawk
!ÚzkýRežim: zap
- V AWK jsou všechna pole asociativní (včetně těch indexovaných celými čísly), a tedy neuspořádaná. Při indexování pole číslem se číslo nejprve převede na řetězec.
- Je velmi často využívána syntaktická zkratka, že literál regulárního výrazu (např. /^a/) se automaticky rozšíří na test načteného řádku (např. „($0 ~ /^a/)“).
- V AWK je středník potřeba jen k oddělení příkazů na jednom řádku, přesto z důvodu přehlednosti doporučuji ho psát na konci příkazu s výjimkou případu, kdy jde o jediný příkaz v bloku, k němuž těsně přiléhají složené závorky, např. „{print $0}“.
- Skalární proměnné se do funkcí předávají hodnotou, pole odkazem.
- Hodnoty ARGC a ARGV je možno za běhu skriptu bez omezení měnit, a tím ovlivňovat, které další soubory gawk či mawk otevře. Na již otevřené soubory to ale nemá vliv.
- Používání koprocesů vyžaduje pečlivou synchronizaci mezi procesy. Existují dvě situace, které vedou k zamrznutí programu a musíte se jim vyhnout: 1) Pokus o přečtení řádku z výstupu koprocesu, zatímco koproces nezapisuje, ale sám čeká na další vstup. 2) Zapsání velkého množství dat (cca od desítek kilobajtů) na vstup koprocesu, která koproces nenačte. (V takovém případě se naplní buffer roury.)
- V literálech regulárních výrazů je nutno odzvláštňovat obyčejná lomítka, a to dokonce i uvnitř hranatých závorek, např. „a*[x\/y]+“, v dynamických regulárních výrazech je není nutno odzvláštňovat.
- Chcete-li příkaz pokračovat na další řádce, vložte před konec řádky „\“.
- Obsahuje-li skript pouze vzorky BEGIN a žádné jiné, AWK nebude otevírat vstupní soubory a po vykonání průchodu BEGIN okamžitě skončí. Toho lze využít k napsání programu, který vstup nezpracovává.
- Nestojí-li za sekvencí zpětných lomítek v řetězci náhrady funkcí sub() a gsub() „&“, chová se toto odzvláštňování nelogicky – méně než tři zpětná lomítka se použijí tak, jak jsou, a každá čtveřice zpětných lomítek se zredukuje na dvě zpětná lomítka a zbytek sekvence se bere jako od začátku, takže např. 6 zpětných lomítek (v řetězci zapsaných jako 12) zapíše při náhradě čtyři zpětná lomítka, protože první čtyři lomítka se zredukovala na dvě a zbylá dvě se vzala tak, jak jsou. Toto neplatí ve funkci gensub(), ta se chová konzistentně a každou dvojici zpětných lomítek zredukuje na jedno, ať za ní následuje ampresand nebo ne. Pokud tedy potřebujete nahrazovat shody regulárního výrazu zpětnými lomítky, doporučuji vždy řetězec náhrady předem otestovat a pamatovat, že funkce sub() a gsub() zachází se zpětnými lomítky, za kterými nenásleduje ampresand, jinak než funkce gensub().
- Soubory s konci řádek CR-LF (typicky z Windows) lze snadno zpracovat při nastavení „RS="\r\n"“; analogicky soubory s řádky ukončenými LF lze zpracovat s nastavením „RS="\r"“.
- GNU awk plně podporuje znaky UTF-8 cca do hodnoty 50000. Od určité hodnoty dál s nimi pracuje chybně, takže není vhodný např. ke zpracování emoji-znaků.
- Článek na Wikipedii
- Přednáška Lukáše Bařinky „(g)awk in a nutshell“
- Článek na ABC Linuxu
- Oficiální manuál: Řetězcové funkce (reference) (anglicky)
- Oficiální manuál od GNU (anglicky)
- man 1 gawk (anglicky)
- Balíček gawk
- Video „Using AWK to filter Data from Fields in Linux“ (anglicky)
- Video „Controlling Array Sorting in AWK“ (anglicky)
- Awk tutorial (anglicky)
- Video „awk command | Powerful Text Manipulation Tool“ (anglicky)
- Video: UNIX – awk explained (anglicky)
- TL;DR stránka (anglicky)
!ÚzkýRežim: vyp
# lkk retence – načte celý standardní vstup do paměti a po uzavření vstupu jej vypíše na výstup
#!/bin/bash
exec gawk -b -e 'BEGIN {RS = FS = "^$"; ORS = "";} {print}'
!ÚzkýRežim: zap
V této verzi kapitoly chybí:
!KompaktníSeznam:
- nic
Tato kapitola záměrně nepokrývá:
!KompaktníSeznam:
- nic
!ÚzkýRežim: vyp