Úvod do AWK: proměnné, operátory a první kroky

6. 9. 2006
Doba čtení: 5 minut

Sdílet

Pokud máte pocit, že Vám při zpracování textu v Linuxu něco chybí, dost možná je AWK právě to, co hledáte. Je poměrně snadný, hodí se na zpracování tabulkových dat, kde disponuje velkou flexibilitou, a samozřejmě podporuje regulární výrazy. V několika málo článcích se vás budu snažit s AWK seznámit.

První kroky

Pro kratší skriptíky a na testování se hodí spouštět AWKové programy z příkazové řádky.

awk '{ print "Ahoj světe"; }'

Jako první parametr bere awk zdrojový text, nikoliv název souboru. Zdrojový text je vždy ve složených závorkách a vykonává se postupně pro každou řádku ze vstupu. V našem případě je tedy celým zdrojovým kódem příkaz print "Ahoj světe", který je ukončený středníkem. Když program spustíte, bude awk čekat na vstup a na každou řádku, kterou mu zadáte, vypíše Ahoj světe.

Řetězce

Stringy neboli textové řetězce se v AWKu píší do uvozovek.

"Winston Churchill"

Existuje ale několik znaků, které nejdou napsat přímo. Nejdůležitějším příkladem je uvozovka:

"...a řekl: "Nádhera""
"...a řekl: \"Nádhera\""

Pokud bychom ji psali přímo, nebylo by poznat, kde string končí a kde začíná. Přesněji řečeno, začínal a končil by jinde, než chceme. Pokud potřebujeme napsat ve stringu uvozovku, musíme těsně před ní napsat zpětné lomítko. Tím ale utrpělo zpětné lomítko, co kdybychom ho chtěli napsat těsně před konec stringu?

"a na konci je \"
"a na konci je \\"

Teď by si AWK zase myslel, že se naše uvozovka má vypsat a marně by hledal uvozovku, která má string ukončit. Proto pokud chceme napsat zpětné lomítko, musíme jej opět uvodit zpětným lomítkem. Existuje i několik dalších znaků, které se dají pomocí lomítka psát:

\n - nová řádka (line feed, pro Unix/Linux)
\r - nová řádka (carriage return, spolu s \n pro Windows, samostatně pro Mac)
\t - tabulátor

Některé další znaky mají další významy, pozor na to. Existuje i tzv. prázdný string, někdy se mu také říká NULL string, který se zapíše jako prázdný pár uvozovek.

Proměnné

Proměnné v AWKu nejsou deklarované a ani se deklarovat nedají. To znamená, že když potřebujete použít proměnnou, stačí ji začít používat. Jméno musí začínat písmenem a další znaky musí být písmena, číslice nebo podtržítko. Není potřeba je uvozovat např. $ jako v některých jazycích.

#dobře
jmeno = "Winston";
a15 = "nic";
a_b_c_d_e = "Abeceda";
#špatně
5kapitola = "Tělem i duší";

Přináší to s sebou však i jednu velkou záludnost. Může se totiž stát, že se v názvu proměnné překlepnete a AWK si bude myslet, že chcete použít jinou proměnnou.

print jemno;

Pokud nepřiřadíte proměnné explicitně nějakou hodnotu, bude obsahovat prázdný string.

Desetinná nebo celá čísla jako samostatný typ neexistují. Vyjadřují se pomocí stringu a jako usnadnění se mohou psát bez uvozovek

rok = 2006; nebo rok = "2006";
pi = 3.1415; nebo pi = "3.1415";
minusjedna = -1; nebo minusjedna = "-1";

S proměnnými obsahujícími čísla můžeme dělat všechny obvyklé operace, můžeme je tedy sčítat, odčítat, násobit, dělit a počítat z nich modulo pomocí operátoru %. Komentáře, pokud by jste je potřebovali, v AWKu začínají # a končí na konci řádku.

{ pi = 3.1415; dve_pi = pi * 2.0; print dve_pi; }     # vytiskne dvojnásobek PI

Prázdný řetězec se, má-li být číslo, chová jako nula. Pokud ho použijeme ve významu stringu, je opravdu prázdný.

{ prazdny = ""; print prazdny; }                                      # nevytiskne nic
{ cislo = 1; prazdny = ""; cislo = cislo + prazdny; print prazdny; }  # vytiskne vždy 1

Pokud bychom chtěli například po zadání každé řádky vytisknout její číslo, můžeme to udělat následovně:

{ radka = radka + 1; print radka; }

Operátory poprvé

Kromě normálních a známých +, -, /, * a potažmo %, jejichž význam je zřejmý, bych rád vysvětlil funkci i několika dalších operátorů.

Jedno z prvních míst, kde by mohl vzniknout problém, je operátor rovná se =, který zajišťuje přiřazení. Je ho potřeba pečlivě odlišovat od operátoru ==, který slouží k porovnání. Prostoru pro omyl je ale naštěstí poměrně málo, protože na místě == se mi v AWKu obyčejné = protlačit nepodařilo. Naopak už bohužel ano.

{ cislo == 1; print cislo; }     # nevypíše nic, číslo je prázdný string a operátor == porovnává, nikoliv přiřazuje.

Užitečnou skupinou operátorů jsou i zkrácené operátory, které jsou stejné jako např. v jazyce C/C++ a které umožňují některé výrazy zapsat úsporněji. Jsou to -=, +=, /=, *= a %=. Význam bude snad zřejmý z příkladu. Oba zápisy mají identický význam:

cislo *= 2;
cislo = cislo * 2;

Operátory -- a ++ zmenšují nebo zvětšují proměnnou o 1. Je ovšem důležité, jestli se jeden z těchto dvou operátorů nachází před nebo za proměnnou. Nachází-li se před, je zvětšení či zmenšení provedeno dřív než se proměnná použije v dalších výpočtech ve stejném výrazu či na stejné řádce. Je-li uveden za proměnnou, provede se zvětšení či zmenšení až po vyhodnocení výrazu. V různých variacích by tedy náš příklad s počítáním řádek vypadal následovně:

{ radka++; print radka; }      # v pořádku (1, 2, 3...n)
{ print ++radka; }             # v pořádku, ekvivalentní zápis (1, 2, 3...n)
{ print radka++; }             # taky v pořádku, ale číslováno od nuly (0, 1, 2...n)

Pokud vás pozice ++ a -- před a za proměnnou zmátla, nic si z toho nedělejte. Není problém psát ++ a -- jen osamoceně na zvláštní řádky, kde je pozice operátoru vůči proměnné nezajímavá. Osobně si myslím, že to čitelnosti programu spíš prospívá. Míchání více ++ a -- na jedné řádce nedoporučuji.

Posledním operátorem, který v tomto díle zmíním, je operátor spojení stringů. Ten je užitečný, pokud máte dva stringy a chcete je spojit dohromady. Mohlo by vás napadnout, že by to mohl být operátor +. Ten už ale používají číselné operace! Některé jazyky používají prostou tečku. AWK používá mezeru.

{ jmeno = "Winston"; prijmeni = "Churchill"; print jmeno " " prijmeni; }

Sloupce

Řádky, které AWK dostává na zpracování, rozděluje do sloupců. Jako sloupce chápe kusy textu, které jsou odděleny mezerou nebo tabulátorem. Tyto sloupce jsou nám pak přístupné přes speciální „zabudované“ proměnné $1, $2 a podobně až k poslednímu sloupci. V $0 najdeme text celé řádky.

{ print $1 }     # vytiskne vždy první slovo ze zadaného textu, tedy první sloupec

Kdybychom si z výpisu ls -l chtěli vypsat jen jméno souboru a vlastníka, mohli bychom to udělat takto:

ict ve školství 24

{ print $3 " " $9; }

Nesmíme si ale zapomenout přesměrovat výstup z ls  na vstup awk.

ls -l | awk '{ print $3 " " $9; }'

Shrnutí

  • Příkazy se oddělují středníkem, odřádkovat nestačí. Program začíná a končí složenými závorkami, do kterých se také uzavírají bloky kódu..
  • Vstup je rozdělován na řádky.
  • Znaky, které nejdou z různých důvodů normálně zapsat, se ve stringu uvozují zpětným lomítkem.
  • I číselné proměnné jsou vlastně stringy. Početní operace se dají se stringy vyjadřujícími číslo provádět zcela intuitivně.
  • Stringy se spojují mezerou.
  • Obsah sloupců je přístupný zabudovanými proměnnými $1, $2  ap.

Závěrem

Příště si povíme, jak ovlivňovat dělení po řádkách a po sloupcích a vysvětlíme si řídící struktury AWKu. Pokud máte k článku připomínky nebo otázky, přispějte do diskuse a já se budu snažit odpovídat.

Autor článku