Tutorial awk

La materia SO[1. Sisteme de operare] învățăm printre altele Unix, unelte Unix și avem și teme cu anumite scripturi. Printre altele, sed, grep și awk. La primele două nu am fost pe fază și nu am scris tutorial, dar voi încerca să explic un pic awk acuma (pentru că există greșeli în doc­u­men­tația pe care ne-a dat-o proful).

Awk este un limbaj de scripting spe­cial­izat pe pre­lu­crarea datelor de tip text. Awk consideră un fișier ca fiind format din în­reg­istrări (delimitate prin ’n’, adică linie nouă Unix-style, dar se poate schimba), iar fiecare în­reg­is­trare are câmpuri, care sunt delimitate prin spațiu deobicei (dar se poate schimba aceasta). Un program awk constă din:

condition { action }

In­ter­pre­torul trece peste fiecare linie din fișierele de intrare și, dacă condiția este adevărată, execută acțiunile asociate. Acțiunile sunt secvențe de forma:

if( expression ) statement [ else statement ]
while( expression ) statement
for( expression ; expression ; expression ) statement
for( var in array ) statement
do statement while( expression )
break
continue
{ [ statement ... ] }
expression              # commonly var = expression
print [ expression-list ] [ > expression ]
printf format [ , expression-list ] [ > expression ]
return [ expression ]
next                    # skip remaining patterns on this input line
nextfile                # skip rest of this file, open next, start at top
delete array[ expression ]# delete an array element
delete array            # delete all elements of array
exit [ expression ]     # exit immediately; status is expression

În expresii putem folosi toți operatorii clasici. Vari­abilele sunt automat in­ițial­izate cu stringul gol sau cu 0, în funcție de necesități. Există câteva variabile și funcții speciale (nu o să le enumăr pe toate, read man for that):

FS: expresie regulară care definește cu ce se separă câmpurile

NF: numărul de câmpuri din în­reg­is­trarea curentă

NR: numărul ordinal al în­reg­istrării curente. Se referă la numărul global, adică câte linii au fost procesate până acum, nu la numărul liniei din cadrul fișierului curent

FNR: numărul în­reg­istrării în fișierul curent

FILENAME: ghici

ARGC: numărul de argumente de la linia de comandă

ARGV: tabloul cu argumente

length(string): Returnează lungimea stringului

substr(string,start,[len]): Returnează un string trunchiat conform para­metrilor

Pat­ter­nurile (șabloanele) sunt formate din expresii regulate și expresii re­laționale (comparatii) care se aplica campurilor si care determina asupra căror linii se aplica acțiunile date. Două patternuri mai speciale sunt BEGIN și END, care sunt adevărate înainte de prima linie citită, respectiv după ultima linie citită (global, nu pe fișier!). Mai este si patternul gol, care se aplica la orice linie. Sunt două moduri de a folosi programe awk: primul de a da ca parametru la awk secvența de in­strucți­u­ni. Acesta devine destul de urât în momentul în care avem programe mai lungi. A doua metodă este să îi dăm la awk un fișier „scenariu”, care conține in­strucți­u­nile:

 awk 'instructions' file1 file2 awk -f file_scen file1 file2

unde file1 și file2 sunt fișierele pe care vrem să executăm programul nostru.

Și acum să trecem la învățatul concret, pe exemple:

Sa se afiseze liniile din fisierele date ca parametru care contin un acelasi cuvint aflat in pozitii con­sec­u­tive. Pentru liniile respective sa se afiseze si numarul liniei in cadrul fisierului din care face parte.

{
word = "" # resetam cuvantul memorat la inceputul fiecarei linii
for (i = 0; i< NF; i++) {    # trecem peste toate liniile
    if (word == $(i+1) ) {   # $(i+1) se refera la campul al i+1-lea din rand
        print "Fisierul " FILENAME ", linia " FNR ": " $0
        break
    }
    word = $(i+1)            # schimbam cuvantul memorat la campul curent
}
}

Sa se afiseze din fiecare fisier dat ca parametru numerele liniilor care au lungimea cel putin 10. De asemenea sa se afiseze continutul liniilor respective, mai putin primele 10 caractere. La terminarea analizei unui anumit fisier se va afisa numele fisierului si numarul de linii care au fost afisate.

BEGIN { # regula care se va executa doar la inceput
    filename = ARGV[1]  # initializam variabila cu a doua compononeta a parametrilor
                        # de la linia de comanda, deci cu primul fisier pe care va opera awk
                        # deoarce FILENAME inca nu a fost initializat
}
{
if (FILENAME!=filename) {  # cand trecem la un fisier nou afisam contorul, numele
                        # fisierului si resetam variabilele
    print "In the " filename " there were " count " lines longer than 10 characters"
    count=0
    filename=FILENAME
}
if (length($0) > 10) {  # $0 se refera la tot sirul
    print "Line " FNR " is longer than 10 characters: " substr($0,10)  # FNR ne zice pe ce linie suntem in cadrul fisierului curent
    count++
}
}
END {   # la sfarsit afisam statisticile despre ultimul fisier pe care am lucrat
    print "In the " filename " there were " count " lines longer than 10 characters"
}

Sa se afiseze numarul de fisiere, numarul total de cuvinte si numarul mediu de cuvinte din fisierele date ca parametri.

BEGIN {
    filename = ARGV[1]
    count = 1
}
{
if (filename != FILENAME) {
    filename = FILENAME
    count++
}
wordCount += NF # NF contine cate campuri (deci cuvinte in cazul nostru) sunt pe linia curenta
}
END {
    print "Au fost " count " fisiere "
    print "Numarul total de cuvinte este " wordCount ", iar media este " wordCount/count
}

Edit: 4. Pentru fiecare fisier dat ca parametru al comenzii awk, sa se afiseze numarul maxim de linii con­sec­u­tive identice din fisier. Se va afisa continutul liniei, numarul de repetari si numele fisierului.

    BEGIN {
        filename = ARGV[1]
        linie = ""
        count = 0
        max = 0
    }
    {
    if (filename != FILENAME) {
        if (count > max)
            max = count
        print "In fisierul " filename " cea mai repetata linie este " linie " care se repeta de " max " ori"
        count = 0
        linie = ""
        filename = FILENAME
    }
    if (linie != $0 ) {
        linie = $0
        print "max1 " max " count " count
        if (count > max)
            max = count
        count = 1
        print "max2 " max " count " count
    }
    else {
        count = count +1
    }
    }
    END {
        if (count > max)
            max = count
        print "In fisierul " filename " cea mai repetata linie este " linie " care se repeta de " max " ori"
    }

Sper că v-a fost de folos acest tutorial. Dacă aveți nelămuriri, lăsați un comment mai jos.