Laborator 10 - Shell scripting

Suport laborator

Cuvinte cheie: date, prelucrare, means, ends, parsare, prezentare, date tabelare, separator de câmpuri, cut, awk, IFS, while read, expresii regulate, metacaractere, grep, shell scripting, for, if, rezultate numerice

  • Suport (Introducere în sisteme de operare)
    • Capitolul 12 – Shell scripting
      • Secțiunile 12.4, 12.5, 12.6, 12.9
    • Puteți descărca fișierul PDF aferent de aici.
    • Capitolul 1 – Introduction to Regular Expressions
    • Capitolul 2 – Basic Regular Expression Skills

Repository laborator

Pentru a descărca fișierele necesare acestui laborator deschidem un terminal (folosim combinația de taste Alt+Ctrl+t) și clonăm repository-ului oficial de USO. Folosim comanda

student@midgard:~$ git clone https://github.com/systems-cs-pub-ro/uso

În directorul /home/student/usot/lab10 găsim fișierele necesare pentru laboratorul 10. Accesăm acel director folosind comanda

student@midgard:~$ cd uso/lab10
student@midgard:~/uso/lab10$ pwd
/home/student/uso/lab10

Demo

În continuare, împreună cu asistentul veți parcurge câteva exerciții de tip demo/tutorial.

Expresii regulate

Expresiile regulate sunt folosite pentru căutarea/selecția anumitor intrări în fișiere text și pentru parsarea informațiilor din fișiere text. În procesarea expresiilor regulate, matching-ul se face pe fiecare linie din text. Într-o expresie regulată, anumite caractere au rol special precum caracterele de mai jos.

Caracter Efect în expresie regulată
^ început de linie
$ sfârșit de linie
. orice caracter
* expresia anterioară de oricâte ori, posibil niciodată
+ expresia anterioară de oricâte ori, cel puțin o dată
[ajt] orice caracter din setul de caractere a, j, t
[^ajt] orice caracter mai puțin caracterele a, j, t
| expresia anterioară sau expresia de după (una dintre cele două expresii)
? expresia anterioară o dată sau niciodată

Găsiți un tool online aici care va permite să vizualizați efectul expresiilor regulate în mod interactiv. Puteți să îl folosiți pentru a vă obișnui cu scrierea de regexp-uri.

Expresii regulate vs globbing

Construtie RegExp Glob
. orice carcater caracterul .
.* orice caracter de oricâte ori, posibil niciodată orice incepe cu .
? expresia anterioară o dată sau niciodată orice caracter
a+ carcaterul a de oricâte ori, cel puțin o dată carcaterul a urmat de +

Expresii regulate & grep

În directorul din repository aferent laboratorului există un fișier students.txt pe care îl vom folosi ca suport pentru comenzi cu expresii regulate. Acest fișier conține o listă de studenți cu numele complet al studenților (prima coloană), grupa din care fac parte (a doua coloană) și diverse note la USO (nota finală - a treia coloană, nota la testul grilă - a patra coloană - și nota la testul practic - a cincea coloană), câmpuri separate prin caracterul tab.

Pentru căutarea și selectarea de linii din fișiere text folosim comanda grep care folosește, la rândul ei, expresii regulate. Astfel, dacă dorim să selectăm studenții care au litera z în numele lor, folosim comanda

student@midgard:~/uso/lab10$ grep 'z' students.txt 
GHECENCO F. Răzvan	312CC	8	8.75	4.67
MARIN N. Răzvan	312CC	5	3.5	4.2

Este recomandat ca argumentul de tip expresie regulată transmis comenzii grep să fie plasat între apostrofuri (') pentru a fi escapat. În felul acesta caracterele din expresia regulată vor fi transmise întocmai comenzii grep și nu vor fi interpretate de shell.

Click pentru detalii legate de apostrofuri și ghilimele

Click pentru detalii legate de apostrofuri și ghilimele

În shell este recomandată folosirea ghilimelelor (, (double) quotes) și a apostrofurilor (', single quotes) pentru escapare. Escapare înseamnă că nu vor fi interpretate special caracterele speciale din shell; adică vor fi interpretate drept caractere obișnuite. Exemple de caractere speciale în shell sunt:

  • $: folosit pentru expandare: expandarea valorii unei variabile, expandarea unei comenzi, substituție aritmetică
  • *: folosit în globbing: înseamnă orice caracter de oricâte ori
  • >, <: folosite pentru redirectare
  • &, |: folosite pentru trimiterea unei comenzi în background sau pentru înlănțuirea unei comenzi; sau ca parte a operatorilor de secvențiere condiționată (|| și &&)
  • ;: folosit pentru secvențierea comenzilor
  • (, ): folosite pentru crearea de subshell-uri
  • , ', \: folosite pentru escapare

Diferența dintre ghilimele și apostrofuri constă în interpretarea caracterului dolar ($, dollar). Între ghilimele, caracterul dolar își păstrează rolul special, pe când între apostrofuri este considerat un caracter ca oricare altul.

De exemplu, dacă dorim să afișăm valoarea variabilei USER, vom folosi construcția

student@mjolnir.local:~$ echo "$USER"
student

Va fi afișată valoarea variabilei USER întrucât am folosit construcția $USER între ghilimele.

Dacă însă folosim construcția $USER între apostrofuri, ca în continuare

student@mjolnir.local:~$ echo '$USER'
$USER

atunci va fi afișat chiar șirul $USER. Asta pentru că apostrofurile, spre deosebire de ghilimele, escapează inclusiv caracterul dolar.

Doar că expresia z nu se potrivește (nu face match) pe litera Z (majusculă). Pentru a selecta studenții al cărore nume conține atât litera z cât și Z (majusculă) folosim expresia regulată [zZ] și comanda

student@midgard:~/uso/lab10$ grep '[zZ]' students.txt 
PAJARCU Z.P. Raul-Constantin	312CC	2	0.5	1.7
GHECENCO F. Răzvan	312CC	8	8.75	4.67
MARIN N. Răzvan	312CC	5	3.5	4.2
ZINCULESCU C. Marius-Valentin	313CC	7	4.75	5.7

Expresia regulată [zZ] se potrivește pe setul de caractere compus din litera z cât și Z (majusculă). Cu aceasta au apărut două linii noi: o linie conține studentul cu numele de familie ZINCULESCU iar cealaltă linie conține ințialele Z.P. ambele cu litera Z (majusculă).

Dacă vrem să selectăm studenții al căror nume de familie începe cu litera F, atunci vom folosi expresia regulată ^F denotând litera F (majusculă) la început de rând. Vom folosi, așadar, comanda de mai jos:

student@midgard:~/uso/lab10$ grep '^F' students.txt 
FLOREA N. Monica-Andreea	313CC	9	7.5	8.5
FULGER P. Alexandru-Gabriel	315CC	7	4.25	5

Dacă vrem în schimb să selectăm studenții al căror prenume începe cu litera F trebuie să privim lucrurile altfel. Urmărind textul putem observa că prenumele sunt precedate fie de caracterul spațiu (blank) fie de caracterul minus (-). Pentru început vom folosi expresia regulată [ -]F care face match pe caracterul spațiu sau minus urmat de caracterul F majusculă. Comanda aferentă este:

student@midgard:~/uso/lab10$ grep '[ -]F' students.txt 
ONEA I. Flavia-Katilina	311CC	7	6.5	4.33
PLEȘEA Fl. Alexandru	311CC	9	9.25	7.9
GHECENCO F. Răzvan	312CC	8	8.75	4.67
EPURE P. Andi-Florin	314CC	8	9.5	3.67
NEACȘU C. Florin-Mărgărit	314CC	10	9	9
COSTEA I. Florin Traian	315CC	4	3.5	3.7
CHIȚESCU E. Bogdan-Florentin	315CC	9	7.75	6.89

Observăm, însă, că se face match și pe șirul Fl. și pe șirul F. reprezentând inițiale. Știm că un prenume conține doar litere mici și că nu se încheie cu punct (., dot). Atunci extindem expresia regulată de mai sus la expresia [ -]F[a-z]+[^\.]. Expresia regulată face match pe un șir care:

  • începe cu unul dintre caracterele spațiu (blank) sau minus (-);
  • continuă cu litera F (majusculă)
  • continuă cu litere mici (folosind setul [a-z])
  • literele mici de oricâte ori cel puțin o dată (folosind caracterul plus (+))
  • continuă cu orice caracter diferit de punct (., dot), folosind expresia [^\.]. Folosim backslash (\) pentru a escapa caracterul punct (., dot); altfel ar fi însemnat orice caracter, așa cum înseamnă într-o expresie regulată.

În expresiile regulate obișnuite (basic regular expressions), anumite caractere își pierd rolul lor special. Este cazul caracterului +, dar și al caracterelor ?, {, (, ) și |. Pentru a-și păstra rolul special, acestea trebuie precedate de backslash în construcții de forma \+, \?, \{, \(, \) și \|.

Găsiți această precizare în pagina de manual a comenzii grep; căutați șirul Basic vs Extended.

Astfel, comanda grep pentru a extrage studenții al căror prenume începe cu litera F este

student@midgard:~/uso/lab10$ grep '[ -]F[a-z]\+[^\.]' students.txt 
ONEA I. Flavia-Katilina	311CC	7	6.5	4.33
EPURE P. Andi-Florin	314CC	8	9.5	3.67
NEACȘU C. Florin-Mărgărit	314CC	10	9	9
COSTEA I. Florin Traian	315CC	4	3.5	3.7
CHIȚESCU E. Bogdan-Florentin	315CC	9	7.75	6.89

Dacă ați fi dorit să faceți match pe o literă (mică sau mare) ați fi folosit construcția [a-zA-Z]. Dacă ați fi dorit să faceți match pe o literă (mică sau mare) sau pe o cifră, ați fi folosit expresia [a-zA-Z0-9].

tr & sed

Utilitarul tr permite translatarea, ștergerea și manipularea caracterelor primite la intrare. După cum am văzut în laboratorul precedent, sed este un stream editor ce poate efectua transformări la nivel de string asupra unui text primit la intrare. În plus, sed poate primi expresii regulate ca argument de căutare.

Diferența dintre tr și sed este că primul efectuează transformări la nivel de caracter, pe când al doilea efectuează transformări la nivel de string. Din acest motiv putem spune că șed este un tr mai avansat, tr++ :).

Spre exemplu, folosinf atât tr, cât și sed, să înlocuim caracterul ',' cu TAB în student.csv:

student@ubuntu:~/uso/lab10$ cat students.csv | tr , "\t" > students.out
student@ubuntu:~/uso/lab10$ cat students.out 
VLĂDUȚU I. Liviu-Alexandru	311CC	6	3.5	5.22
GEORGIU V. Alexandra-Maria	311CC	10	10	9.67
PĂUNOIU N. Gabriel	311CC	7	6.5	3.5
BĂCÎRCEA A. Andrei	311CC	7	5.5	4.44
[...]
 
student@ubuntu:~/uso/lab10$ sed 's/,/\t/g' students.csv > students.out 
student@ubuntu:~/uso/lab10$ cat students.out 
VLĂDUȚU I. Liviu-Alexandru	311CC	6	3.5	5.22
GEORGIU V. Alexandra-Maria	311CC	10	10	9.67
PĂUNOIU N. Gabriel	311CC	7	6.5	3.5
BĂCÎRCEA A. Andrei	311CC	7	5.5	4.44
[...]

Observăm că cele două comenzi au același efect. Pentru a înlocui șiruri de caractere, tr nu mai oferă funcționalitatea dorită întrucât el face o mapare 1-la-1 între caracterele setul ce trebuie înlocuit și setul ce înlocuiește. În exemplul de mai jos, 1 este translatat în 2 indiferent unde apare, iar C în A:

student@ubuntu:~/uso/lab10$ cat students.csv | tr "311CC" "312CA" > students.out
student@ubuntu:~/uso/lab10$ cat students.out 
VLĂDUȚU I. Liviu-Alexandru,322AA,6,3.5,5.22
GEORGIU V. Alexandra-Maria,322AA,20,20,9.67
PĂUNOIU N. Gabriel,322AA,7,6.5,3.5
BĂAÎRAEA A. Andrei,322AA,7,5.5,4.44
[...]
 
student@ubuntu:~/uso/lab10$ sed "s/311CC/312CA/g" students.csv > students.out
student@ubuntu:~/uso/lab10$ cat students.out 
VLĂDUȚU I. Liviu-Alexandru,312CA,6,3.5,5.22
GEORGIU V. Alexandra-Maria,312CA,10,10,9.67
PĂUNOIU N. Gabriel,312CA,7,6.5,3.5
BĂCÎRCEA A. Andrei,312CA,7,5.5,4.44
[...]

cut & awk

Am văzut în laboratoarele precedente cum putem extrage informații structurate pe linii și coloane folosind utilitarul cut.

student@ubuntu:~/uso/lab10$ cat /etc/passwd | cut -d':' -f1,6 | head -3
root:/root
daemon:/usr/sbin
bin:/bin

Utilitarul awk permite aceleași acțiuni ca și cut, dar funcționalitatea sa este mai extinsă. Spre exemplu, awk poate folosi o expresie regulată ca delimitator, pe când cut acceptă un singur caracter:

student@ubuntu:~/uso/lab10$ netstat -i
Kernel Interface table
Iface   MTU Met   RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0       1500 0      1955      0      0 0           521      0      0      0 BMRU
lo        65536 0       359      0      0 0           359      0      0      0 LRU
 
student@ubuntu:~/uso/lab10$ netstat -i | cut -d' ' -f1,2
Kernel Interface
Iface 
eth0 
lo 
 
student@ubuntu:~/uso/lab10$ netstat -i | awk -F' *' '{print $1,$2,$4}'
Kernel Interface 
Iface MTU RX-OK
eth0 1500 2029
lo 65536 359

Awk este considerat un fel de limbaj de programare ce vizează procesarea text. Există script-uri awk complexe ce se aseamănă programelor C. Printre altele, awk permite implementarea și apelarea de funcții.

sort & uniq

Dacă dorim să sortăm studenții din fișier în ordinea numelor vom folosi comanda sort:

student@mjolnir:~/uso/lab10$ sort students.csv 
ALECU C. Ionuț-Gabriel,312CC,7,4.5,6.4
ASĂVOAEI P. Cătălin,315CC,8,6.75,7
BĂCÎRCEA A. Andrei,311CC,7,5.5,4.44
BADEA P. Bogdan,314CC,4,2.75,1.56
[...]

Cateva optiuni des folosite cu sort sunt:

  • -t specifica separatorul
  • -k specifica index-ul, sau cheia, coloanei dupa care vrem sa sortam intrarile
  • n sortare numerică (implicit este alfabetica)
  • -r sortare inversa (implicit crescator)

O sursă frecventă de greșeli este folosirea construcției -k 3 în loc de -k 3,3 atunci când folosim mai multe chei de sortare. Dacă folosim -k 3 atunci cheia de sortare este dată de toate coloanele începând cu a 3-a coloană. În acest caz viitoare opțiuni de chei de sortare nu mai sunt luate în considerare.

Vezi și discuția de aici.

Dorim sa facem o sortare numerica descrescatoare după notă, urmata de o sortare alfabetică după grupă (adică să fie toate notele de 10 la început dar sortate în ordinea grupelor). Astfel, pentru a sorta intrările în ordinea notei, vom folosi separatorul , (virgulă, comma) și coloana a 3-a pentru cheie:

student@mjolnir:~/uso/lab10$ sort -t ',' -k 3,3nr -k 2,2 students.csv
GEORGIU V. Alexandra-Maria,311CC,10,10,9.67
MUȘATESCU V. Alexandru-Petrișor,311CC,10,8.5,9
RADU L. Alina,311CC,10,10,7.89
GONDOȘ I. Gabriel,312CC,10,9,7.33
[...]

În cazul în care există linii duplicate, putem folosi utilitarul uniq în conjuncție cu sort pentru a le elimina. În acest scop adăugăm o linie duplicata în students.csv:

VLĂDUȚU I. Liviu-Alexandru,311CC,6,3.5,5.22                                                                                                                                                       
GEORGIU V. Alexandra-Maria,311CC,10,10,9.67
[...]
VLĂDUȚU I. Liviu-Alexandru,311CC,6,3.5,5.22
BENE D. Adrian,312CC,9,10,5
[...]
student@mjolnir:~/uso/lab10$ sort -t ',' -k 3,3nr -k 2,2 students.csv | wc -l
93
student@mjolnir:~/uso/lab10$ sort -t ',' -k 3,3nr -k 2,2 students.csv | uniq | wc -l
92
student@mjolnir:~/uso/lab10$ uniq students.csv | wc -l
93

Observăm că uniq elimină liniile duplicate adiacente. De aceea, dacă datele nu sunt sortate, comanda nu are nici un efect.

Shell scripting

Un script shell este un fișier text care conține comenzi și construcții specifice shell-ului. Un script shell începe cu construcția #!/bin/bash, denumită shebang care indică interpretorul scriptului; în cazul de față interpretorul este chiar shell-ul Bash.

Scriptul conține comenzi uzuale folosite în shell și alte comenzi care se regăsesc mai adesea în scripturi: while, if, for. Acestea nu sunt instrucțiuni, ci sunt tot comenzi shell; pot fi folosite, dacă sintaxa este corectă și în linia de comandă.

Argumente din linia de comandă

Un script moștenește variabilele de mediu ale shell-ului părinte precum HOME, BASH, IFS, USER. În script le putem folosi astfel:

#!/bin/bash
 
echo $HOME

Pe lângă aceste variabile, scriptul poate primi o serie de argumente din linia de comandă. Folosind următoarele variabile, putem referi argumentele primite de script din linia de comandă:

  • $* este un string ($1, $2 … $n) format din toate argumentele primite de script
  • $@ este o listă formată din argumentele scriptului
  • $1, $2 … $n reprezintă fiecare parametru din script
  • $0 este numele scriptului
  • $# este numărul de argumente primite din linia de comandă

Spre exemplu, să luăm următorul script arguments.sh:

#!/bin/bash
   echo There are $# arguments to $0: $*
   echo first argument: $1
   echo second argument: $2
   echo third argument: $3
   echo the list of arguments: $@

Să apelăm scriptul cu 3 argumente:

student@mjolnir:~$ ./arguments.sh banane cirese caise castraveti
   There are 4 arguments to arguments.sh: banane cirese caise castraveti
   first argument: banane
   second argument: cirese
   third argument: caise
   the list of arguments: banane cirese caise castraveti

Constructia while read

Pentru parsare în shell folosim construcția while read .... Construcția este urmată de numele variabilelor în care vom reține câmpurile parsate din cadrul fiecărei linii. Folosim la intrare fișierul students.csv aflat în directorul părinte; este un fișier în format CSV (Comma Separated Values) folosind ca separator caracterul virgulă (,, comma). Pentru a extrage doar numele studenților din fișierul de intrare vom rula scriptul extract-name:

student@mjolnir:~/uso/lab10/parse-table-data$ ./extract-name
VLĂDUȚU I. Liviu-Alexandru
GEORGIU V. Alexandra-Maria
PĂUNOIU N. Gabriel
BĂCÎRCEA A. Andrei
[...]

Întrucât formatul de intrare folosește virgulă (,, comma) pe post de separator, am definit în script variabila internă IFS (Internal Field Separator) la valoarea ',', așa cum observăm în linia 3 din scriptul extract-name:

IFS=','

Click pentru informații despre variabila IFS

Click pentru informații despre variabila IFS

Variabila internă shell-ului IFS (Internal Field Separator) definește caracterul sau caracterele care vor fi folosite pentru împărțirea unei linii în câmpuri (splitting).

Variabila internă este folosită ori de câte ori în shell este nevoie de împărțirea unei linii. Un caz uzual de folosire a variabilei IFS este în conjuncție cu construcția while read pentru citirea de linii de la intrarea standard sau dintr-un fișier și împărțirea acestora în câmpuri.

Informații și pe Wikipedia.

Să extindem scriptul pentru a afișa și numele și grupa. Conținutul noului script va fi:

#!/bin/bash
 
IFS=','
while read name group final_grade test_grade practical_grade; do
    echo "$name,$group"
done < students.csv

Pașii de mai sus puteau fi realizați și cu ajutorul comenzii cut. Dar, în cazul parsării folosind construcția while read avem două avantaje:

  • putem afișa coloanele în ce ordine dorim; cut permitea afișarea de coloane doar în ordinea din fișierul de intrare;
  • putem prelucra în continuare, în cadrul construcției while read informația parsată. Spre exemplu, afișarea poate avea forma "Studentul ... face parte din grupa ...".

if statement

Putem extinde script-ul de mai sus pentru a afișa doar studenții care au media > 5.

#!/bin/bash
 
IFS=','
while read name group final_grade test_grade practical_grade; do
    if test "$final_grade" -gt 5; then
        echo "$name,$group,$final_grade"
    fi
done < students.csv

for loop

În bash există și construcții de tipul for. Un exemplu usual este acela de a itera prin conținutul unui director și a face prelucrări asupra fișierelor. Spre exemplu, dorim să facem backup tuturor fișierelor dintr-un director trimis ca parametru în script:

#!/bin/bash
 
for file in $1/*
do
    if test -f $file; then
        stat --print="%a %F %n\n" $file
        cp $file $file.bkp
    fi
done

Exerciții

Pe măsură ce parcurgeți exercițiile, pentru indicații legate de rezolvare, dați click pe barele de tip spoiler care conțin textul Click pentru indicații.

Acolo unde apar, exercițiile pentru karma points vor fi rezolvate și punctate după rezolvarea tuturor exercițiilor. Am plasat o parte din exercițiile pentru karma points în cadrul exercițiilor obișnuite pentru continuitate.

Atunci când creați un script, nu uitați să îi dați drepturi de execuție:

student@mjolnir:~/uso/lab10$ vim myScript.sh
student@mjolnir:~/uso/lab10$ chmod +x myScript.sh

1. Opțiuni pentru grep (4p)

Ne propunem să aflăm care sunt fișierele de tip header (cu extensia .h sau .hpp) din sistem care au cele mai multe macro-uri definite. Pentru aceasta vom folosi comanda grep cu opțiuni specifice și apoi comanda sort pentru a sorta datele în ordinea numărului de apariții de definiții de macro-uri.

Fișierele header dintr-un sistem Unix/Linux se găsesc în ierarhia /usr/include/.

Pașii din continuare sunt detaliați în subsecțiunile exercițiului (subsecțiunile 1.1, 1.2, 1.3 și 1.4). Parcurgeți subsecțiunile pentru indicații legate de acești pași și pentru rezolvarea exercițiului.

1.1. Extragere linii ce conțin definiții de macro-uri (1p)

Pentru început, ca să aveți un punct de plecare, veți extrage liniile ce conțin definiții de macro-uri pentru un singur fișier header dat. Recomandăm să folosiți ca intrare fișierul /usr/include/stdio.h.

Click pentru indicații

Click pentru indicații

O formă simplă de realizare a acestui lucru este folosirea comenzii:

grep '#define' /usr/include/stdio.h

Întrucât preprocesorul C este mai lax, va trebui să construiți o expresie regulată mai acoperitoare ca argument pentru grep. Construiți și apoi transmiteți către grep o expresie regulată care face match pe liniile ce conțin definiții de macro-uri, ținând cont că:

  1. Liniile pot începe cu spații (blank): oricât de multe caractere spațiu posibil nici unul.
  2. Apoi urmează caracterul diez (#, hash).
  3. Apoi urmează oricâte caractere spațiu (blank), posibil nici unul.
  4. Apoi urmează șirul de caractere define.

Parcurgeți secțiunea Expresii regulate pentru ajutor legat de construirea expresiei regulate.

Comanda va avea ca output liniile ce conțin definiții de macro-uri în cadrul fișierului /usr/include/stdio.h.

1.2. Extragere linii ce conțin definiții de macro-uri în ierarhia /usr/include (1p)

În continuare veți extinde comanda anterioară și veți folosi grep cu opțiunea de recursivitate pentru a extrage liniile ce conțin definiții de macro-uri pentru toate fișierele din ierarhia /usr/include/.

Click pentru indicații

Click pentru indicații

Va trebui să modificați comanda anterioară astfel:

  1. Să folosiți opțiunea de recursivitate pentru grep. Vedeți mai jos indicația.
  2. Să folosiți ca argument pentru grep șirul /usr/include/ (reprezentând o ierarhie parcursă recursiv) în locul șirului /usr/include/stdio.h (reprezentând un singur fișier header).

Pentru a afla opțiunea de recursivitate pentru grep, aveți următoarele posibilități:

  1. Căutați în pagina de manual a comenzii grep șirul recursive și localizați opțiunea necesară.
  2. Căutați pe Google după șirul grep recursive option. Puteți da click pe link.

Atunci când o comandă are output de dimensiune mare și vreți să-l vizualizați, puteți să treceți acel output în comanda less. Adică înlănțuiți comanda care generează output-ul de dimensiune mare cu comanda less, în forma

student@mjolnir:~$ grep ... | less

În felul acesta veți putea folosi în cadrul less parcurgere paginată (înainte și înapoi) și căutare în tot output-ul.

Comanda va avea ca output fișierele din ierarhia /usr/include/ și liniile din cadrul fișierelor ce conțin definiții de macro-uri.

1.3. Obținere număr de linii ce conțin definiții de macro-uri în ierarhia /usr/include (1p)

În continuare veți extinde comanda anterioară și veți folosi grep cu opțiunea de recursivitate și cea de contorizare pentru a furniza ca output fișierele din ierarhia /usr/include împreună cu numărul de linii care conțin definiții de macro-uri.

Click pentru indicații

Click pentru indicații

Va trebui să completați comanda anterioară cu opțiunea de contorizare pentru grep. Cu această opțiune output-ul grep se va schimba: nu vor mai fi afișate liniile ce conțin definiții de macro-uri ci, pentru fiecare fișier, numărul de linii ce conțin definiții de macro-uri.

Pentru a afla opțiunea de contorizare pentru grep, aveți următoarele posibilități:

  1. Căutați în pagina de manual a comenzii grep șirul count și localizați opțiunea necesară.
  2. Căutați pe Google după șirul grep count lines. Puteți da click pe link.

Comanda va avea ca output fișierele din ierarhia /usr/include împreună cu numărul de linii ce conțin definiții de macro-uri. Cele două câmpuri sunt afișate în format tabelar cu separatorul două puncte (:, colon). Fișierele care nu au linii care conțin definiții de macro-uri au ca al doilea câmp valoarea 0 (zero).

1.4. Sortare după numărul de linii ce conțin definiții de macro-uri (1p)

În final, veți sorta output-ul comenzii anterioare în funcție de numărul de linii care conțin definiții de macro-uri. Vrem să aflăm care sunt fișierele de tip header din ierarhia /usr/include care conțin cel mai mare număr de definiții de macro-uri.

Click pentru indicații

Click pentru indicații

Pentru aceasta, veți folosi operatorul de înlănțuire de comenzi (|, pipe) și comanda sort cu opțiunile necesare. Adică veți construi un one liner de forma

student@mjolnir:~/uso.git/lab11$ grep ... | sort ...

Pentru comanda sort folosiți opțiunile specifice pentru ca:

  1. Separatorul de câmp (field separator) să fie cel furnizat de output-ul comenzii anterioare, adică două puncte (:, colon).
  2. Sortarea să se facă după al doilea câmp, adică numărul de linii. Adică să folosească pe post de cheie de sortare al doilea câmp.
  3. Sortarea să fie numerică, întrucât al doilea câmp este un număr.

Parcurgeți secțiunea Sortare de date tabelare pentru ajutor legat de folosirea comenzii sort.

Comanda va avea ca output fișierele din ierarhia /usr/include împreună cu numărul de linii ce conțin definiții de macro-uri, ordonate în funcție de numărul de linii. Dacă ați rezolvat corect, fișierele care au cele mai multe linii ce conțin definiții de macro-uri vor ajunge la mii de astfel de apariții; de exemplu fișierul /usr/include/elf.h are mii de linii care conțin definiții de macro-uri.

Dacă doriți, puteți continua comanda pentru a extrage doar primele 10 fișiere care conțin cele mai multe definiții de macro-uri.

2. Expresii regulate (2p)

Ne propunem să sedimentăm noțiunile legate de expresii regulate. Vom folosi grep și expresii regulate pentru a face două prelucrări peste fișierul /usr/share/vim/vim74/tutor/tutor:

  1. Să extragem liniile care încep cu cuvinte de 5 litere.
  2. Să extragem liniile care se încheie cu cuvinte de 5 litere.

Vor rezulta două comenzii grep pentru cele două prelucrări de mai sus.

Vom considera un cuvânt de 5 litere un șir de 5 caractere de tipul literă mică (de la a până la z) sau literă mare (de la A până la Z).

Pentru aceasta, recomandăm să parcurgeți următorii pași:

  1. creați expresia regulată care face match pe șiruri de 5 litere mici sau mari.
  2. Să creați expresia regulată care face match pe cuvinte de 5 litere la început de linie.
  3. Să creați expresia regulată care face match pe cuvinte de 5 litere la sfârșit de linie.

Parcurgeți secțiunea Expresii regulate pentru ajutor legat de construirea de expresii regulate.

Creați expresia regulată care face match pe șiruri de 5 litere mici sau mari.

Click pentru indicații

Click pentru indicații

Recomandăm să urmați pașii:

  1. Creați expresia regulată care face match pe o singură literă mică sau mare. Folosiți construcția pentru set de caractere.
  2. Repetați de 5 ori construcția de mai sus (adică concatenați de 5 ori acea expresie) pentru o expresie regulată care face match pe 5 litere mici sau mari.
  3. Folosiți grep cu această expresie regulată ca argument peste fișierul /usr/share/vim/vim74/tutor/tutor pentru a extrage liniile ce conțin șiruri de 5 litere mici sau mari. Vor fi majoritatea liniilor.

Observați că expresia regulată de mai sus face match pe șiruri de caractere, nu pe cuvinte. Pentru a face match pe cuvinte, vom considera (chiar dacă nu acoperă toate cazurile) că un cuvânt începe sau se termină cu spațiu (blank).

Astfel, creați expresia regulată care face match pe cuvinte de 5 litere la început de linie.

Click pentru indicații

Click pentru indicații

Porniți de la expresia regulată construită mai sus și faceți următoarele actualizări:

  1. Expresia regulată trebuie să facă match pe începutul de linie.
  2. Înainte de cele 5 litere mici sau mari, după începutul liniei se pot găsi caractere spațiu (blank) oricât de multe posibil nici unul.
  3. După cele 5 litere mici sau mari urmează un caracter spațiu, semnificând că e vorba de un cuvânt.

Rulați comanda grep cu expresia regulată actualizată peste fișierul /usr/share/vim/vim74/tutor/tutor pentru a extrage liniile ce conțin cuvinte de 5 litere mici sau mari la începutul liniei. Urmăriți în output-ul comenzii dacă apar doar linii care conțin cuvinte de 5 litere mici sau mari la începutul liniei.

Puteți înlănțui comanda de mai sus cu comanda wc -l pentru a afla numărul de linii pe care s-a făcut match și compara cu ceilalți colegi dacă ați obținut același rezultat.

În final, creați expresia regulată care face match pe cuvinte de 5 litere la sfârșit de linie.

Click pentru indicații

Click pentru indicații

Porniți de la expresia regulată construită la început (care face match pe șiruri de 5 litere mici sau mari) și faceți următoarele actualizări:

  1. Înainte de cele 5 litere mici sau mari se află un caracter spațiu, semnificând că e vorba de un cuvânt.
  2. După cele 5 litere mici sau mari se pot găsi caractere spațiu (blank) oricât de multe posibil nici unul.
  3. Expresia regulată trebuie să facă match pe sfârșit de linie.

Rulați comanda grep cu expresia regulată actualizată peste fișierul /usr/share/vim/vim74/tutor/tutor pentru a extrage liniile ce conțin cuvinte de 5 litere mici sau mari.

La fel ca mai sus, puteți înlănțui comanda de mai sus cu comanda wc -l pentru a afla numărul de linii pe care s-a făcut match și compara cu ceilalți colegi dacă ați obținut același rezultat.

Pentru 1 karma point, simplificați expresiile regulate folosite pentru match de cuvinte de 5 litere la început, respectiv sfârșit de linie, folosind operatorul de repetiție pentru expresii regulate în grep.

Ca să identificați operatorul de repetiție pentru expresii regulate în grep, aveți următoarele posibilități.

  1. Căutați în pagina de manual a comenzii grep șirul Repetition și localizați opțiunea necesară.
  2. Căutați pe Google după șirul grep regular expression repetition. Puteți da click pe link

Observație: Caracterele acoladă ({, }, curly brackets) trebuie escapate în argumentul transmis către grep pentru a a avea rolul lor special într-o expresie regulată. La fel cum este cazul caracterului plus (+).

3. Parsare folosind tab ca separator (2p)

În secțiunea Parsare date tabelare în shell am folosit la intrare fișierul students.csv, fișier format CSV (Comma Separated Values) care folosește virgula (,, comma) pe post de separator de câmpuri. Ne propunem să folosim la intrare fișierul students.txt care folosește caracterul tab pe post de separator de câmpuri.

Pentru aceasta, creați o copie a scriptului extract-name numită extract-name-tab. Noul script (extract-name-tab) va face același lucru ca scriptul inițial (extract-name): va afișa doar numele studenților. Doar că veți opera două modificări pe acest nou script:

  1. Veți folosi fișierul students.txt în locul fișierului students.csv.
  2. Veți folosi ca separator de câmpuri caracterul tab (specific fișierului students.txt) în locul caracterului virgulă (,, comma) (specific fișierului students.csv). Vedeți indicația de mai jos.

Realizați cele două modificări de mai sus și apoi rulați scriptul extract-name-tab.

Click to display ⇲

Click to hide ⇱

Pentru configurarea caracterului tab ca separator de câmpuri, ar trebui, în mod obișnuit, inițializată variabila internă shell-ului IFS (Input Field Separator) la valoarea '\t', la fel ca în linia

IFS='\t'

Doar că shell-ul nu poate interpreta secvențele de escape precum '\t' sau '\n' și trebuie folosit, în Bash, simbolul dolar ($, dollar) înaintea unui șir cu secvențe de escape. Așadar, modul corect de inițializare a variabilei IFS la caracterul tab este

IFS=$'\t'

Mai multe detalii găsiți aici.

În final, la rularea scriptului extract-name-tab veți obține doar numele studenților așa cum se găsesc în fișierul de intrare students.txt; fișierul de intrare folosește tab pe post de separator de câmpuri.

4. Selectare studenți care au nota 10 (2p)

Ne propunem să actualizăm scriptul anterior extract-name-tab pentru a afișa doar numele studenților pentru acei studenți care au nota finală 10 (a treia coloană are valoarea 10).

Va trebui să adăugați în scriptul extract-name-tab condiția pentru ca nota finală să fie 10. Pentru aceasta folosiți construcția if test .... Afișați numele studenților doar dacă este condiția satisfăcută.

Pentru a afla opțiunea de comparație a unei variabile cu un număr (în cazul nostru 10), căutați în pagina de manual a comenzii test după șirul equal. Folosiți opțiunea de comparație pentru întregi.

La rularea scriptului extract-name-tab veți obține numele studenților care au nota finală 10. Va trebui să obțineți 13 studenți care au nota finală 10.

Pentru 1 karma point, actualizați scriptul extract-name-tab astfel încât să primească un argument reprezentând nota finală pe care au obținut-o studenții care vor fi afișați.

Scriptul va verifica faptul că primește exact un argument și că argumentul este un număr cuprins între 1 și 10. Dacă nu primește un argument sau dacă primește ca argument un număr din afara intervalului 1 până la 10, scriptul va afișa un mesaj de eroare și își va încheia execuția.

Exemple de rulare sunt:

$ ./extract-name-tab 10
$ ./extract-name-tab 7
$ ./extract-name-tab 5

Bonus

0. Selectare și sortare studenți care au nota între 6 și 9 (2 karma WoUSO)

Ne propunem să realizăm un script numit extract-sort-grades pentru a afișa numele studenților, grupa și nota finală pentru acei studenți care au nota finală cuprinsă între 6 și 9 inclusiv. Adică valoarea de pe a treia coloană să fie 6, 7, 8 sau 9. Apoi vom sorta intrările în ordinea notei și apoi în ordinea grupei (adică dacă au aceeași notă să fie sortați în ordinea grupei).

Pentru început creați o copie a scriptului extract-name-tab numită extract-sort-grades pe care o veți actualiza pentru a răspunde cerinței de mai sus.

Pașii din continuare sunt detaliați în subsecțiunile exercițiului (subsecțiunile 5.1 și 5.2). Parcurgeți subsecțiunile pentru indicații legate de acești pași și pentru rezolvarea exercițiului.

Va trebui să actualizați scriptul extract-sort-grades în două locuri:

  1. Condiția trebuie să fie de număr cuprins între 6 și 9 (în loc de număr egal cu 10 cum este cazul în scriptul extract-name-tab). Vedeți indicațiile.
  2. Trebuie afișate numele studenților, grupa din care fac parte și nota finală (nu doar numele cu este cazul în scriptul extract-name-tab).
  3. Trebuie sortate intrările obținute în ordinea notelor și în ordinea grupei.

0.1. Selectare studenți care au nota între 6 și 9 (1 karma WoUSO)

Click pentru indicații

Click pentru indicații

Cerința presupune două condiții care trebuie îndeplinite: nota finală să fie mai mare sau egală cu 6 și nota finală să fie mai mică sau egală cu 9.

Pentru a afla care este opțiunea de și logic pentru comanda test, căutați în pagina de manual a comenzii test șirul and și localizați opțiunea necesară.

La fel procedați pentru a afla opțiunile pentru greater than sau less than.

Exemple de folosire pentru comanda test și diversele sale opțiuni sunt mai jos:

# Is 5 equal to 4? False
$ test 5 -eq 4
$ echo $?
1

# Is 5 equal to 5? True
$ test 5 -eq 5
$ echo $?
0

# Is 6 greater than 6? False
$ test 6 -gt 6
$ echo $?
1

# Is 6 greater than or equal to 6? True
$ test 6 -ge 6
$ echo $?
0

# Is 7 less than 10? True
$ test 7 -lt 10
$ echo $?
0

# Is 7 less than 10 and 6 greater than 4? True
$ test 7 -lt 10 -a 6 -gt 4
$ echo $?
0

# Is 7 less than 10 and 6 greater than 7? False
$ test 7 -lt 10 -a 6 -gt 7
$ echo $?
1

# Is 7 less than 10 or 6 greater than 7? True
$ test 7 -lt 10 -o 6 -gt 7
$ echo $?
0

Afișați la ieșirea standard numele, grupa și nota finală a studenților selectați separate prin virgulă.

La rularea scriptului extract-sort-grades veți obține numele studenților, grupa și nota finală pentru studenții a căror notă finală este cuprinsă între 6 și 9.

0.2. Sortare studenți care au nota între 6 și 9 (1 karma WoUSO)

În continuare actualizați scriptul folosind operatorul de secvențiere (|, pipe) și comanda sort cu opțiunile necesare pentru a sorta ieșirea comenzii în ordinea notei și apoi a grupei.

Click pentru indicații

Click pentru indicații

Pentru comanda sort folosiți ca separator de câmp (field separator) caraceterul virgulă (,, comma). Și faceți sortarea după nota finală (al 3-lea câmp) în format numeric și după grupă (al 2-lea câmp).

Puteți folosi comanda sort la finalul comenzii while. Adică ultima linie a scriptului să fie de forma

done < students.txt | sort ...

În final, rularea scriptului extract-sort-grades va conduce la afișarea numelui, grupei și a notei finale pentru studenții care au nota finală cuprinsă între 6 și 9, sortate după nota finală și după grupă.

Pentru 1 karma point, actualizați scriptul extract-sort-grades astfel încât să primească două argumente reprezentând intervalul de note finale în care trebuie să se afle nota pe care au obținut-o studenții care vor fi afișați. La fel ca mai sus, studenții vor fi afișați în ordinea notei și apoi în ordinea grupei.

Scriptul va verifica faptul că:

  • Primește exact două argumente.
  • Ambele argumente sunt numere cuprinse între 1 și 10.
  • Primul argument este strict mai mic decât al doilea.

Dacă cele de mai sus nu sunt satisfăcute, scriptul va afișa un mesaj de eroare și își va încheia execuția.

Exemple de rulare sunt:

$ ./extract-sort-grades 5 9
$ ./extract-sort-grades 1 4
$ ./extract-sort-grades 8 10

1. Conversie de fișiere imagine (3 karma WoUSO)

Dorim să realizăm un script care să automatizeze rescalarea dimensiunii unor fișiere imagine.

În arhiva uso/lab10/media.zip aveți un set de fișiere imagine. Dezarhivați arhiva folosind comanda:

unzip media.zip

Dorim să scalăm dimensiunea acestor fișiere. Pe un fișier oarecare folosiți comanda 'convert pentru a-i schimba dimensiunea. Spre exemplu, comanda urmatoare convertește fișierul input.jpg în fișierul output.jpg.

convert -resize 50% input.jpg output.jpg

Dorim să convertim fișierele din directorul media/. Vrem ca numele noului fișier să se încheie cu .2. Astfel, dacă în variabila f avem numele fișierul inițial, comanda de conversie va fi:

convert -resize 50% "$f" "$f.2"

Având o cale către un fișier, putem obține numele și extensia acetuia folosind comanda basename.

filename=$(basename "$fullfile") #filename.ext
extension="${filename##*.}"      #ext
filename="${filename%.*}"        #filename

Realizați un script numit convert-scale-images care să convertească toate fișierele din directorul media/ în fișiere noi care să se încheie cu .2.

Folosiți for f in * ... pentru parcurgerea fișierelor.

Actualizați scriptul pentru a primi un parametru care indică scalarea conversiei. Adică dacă parametrul este 25% atunci se scalează imaginea la 25%. Înainte și după scalarea imaginii, afișați dimensiunea imaginii la consolă în formatul widthxhigth.

  • Folosiți opțiunea print a lui convert pentru a afișa dimensiunea imaginii într-un anumit format.

Actualizați scriptul pentru a primi ca argument directorul de intrare (în care se găsesc fișierele) și directorul de ieșire (în care se vor găsi fișierele convertite). Dacă directorul de ieșire nu există va fi creat.

Actualizați scriptul să afișeze mesaje de eroare dacă directorul de intrare nu există. In acest caz, veti iesi din script cu un cod de eroare avand valoarea 1.

  • Verificați existența directorului folosind o construcție de forma if test.
  • Pentru a întoarce un cod de eroare din script, folosi comanda exit. | Amintiți-vă cum verificăm codul de succes/eroare întors de un proces.

Pentru 1 karma point, actualizați scriptul ca să valideze dacă argumentul de scalare este în forma XY% unde X și Y sunt cifre. Puteți folosi pentru validare o construcție de forma

echo "$1" | grep <suitable-regex> > /dev/null 2>&1

2. Selectare studenți după notă fracționară (1 karma point)

Ne propunem să selectăm studenții care au obținut la testul grilă notă mai mare sau egală cu 8. Este vorba de coloana a 4-a din fișierele de intrare (students.txt sau students.csv).

Creați un script numit extract-students-test-over-8 care afișează numele, grupa și nota la testul grilă, separate prin virgulă (,, comma) pentru studenții care au obținut o notă mai mare sau egală cu 8 la testul grilă.

Puteți folosi ca intrare fie fișierul students.txt fie fișierul students.csv.

Click pentru indicații

Click pentru indicații

Notele la testul grilă sunt numere fracționare (float). Întrucât în shell nu se pot efectua operații pe numere fracționare (însemnând că nu putem face comparații), va trebui să folosiți utilitarul bc.

Utilitarul bc permite comparații pe numere fracționare afișând 0 în cazul în care comparația este falsă, și 1 în cazul în care comparația este adevărată. bc primește operațiile la intrarea standard. Mai jos sunt două exemple de folosire:

$ echo "1.4 > 2.5" | bc
0
$ echo "2.5 >= 1.14" | bc
1

În cazul primei comenzi, bc primește la intrarea standard șirul 1.4 > 2.5 însemnând comparația este 1.4 mai mare decât 2.5?. Întrucât comparația este falsă, bc afișează 0.

În cazul celei de-a doua comenzi, bc primește la intrarea standard șirul 2.5 >= 1.14 însemnând comparația este 2.5 mai mare sau egal decât 1.14?. Întrucât comparația este adevărată, bc afișează 1.

În urma rulării scriptului extract-students-test-over-8 veți obține numele, grupa și nota la testul grilă, separate prin virgulă, pentru studenții care au obținut o notă mai mare sau egală cu 8 la testul grilă. Output-ul va conține 36 de linii.

3. Selectare studenți după note fracționare (1 karma point)

Ne propunem să selectăm studenții care au obținut la testul grilă notă mai mare sau egală cu 8 (coloana a 4-a) și care au obținut la testul grilă notă mai mică sau egală ca cea de la testul practic (coloana a 5-a).

Actualizați scriptul extract-students-test-over-8 și afișați numele, grupa, nota la testul grilă și nota la testul practic, separate prin virgulă (,, comma) pentru studenții care au obținut o notă mai mare sau egală cu 8 la testul grilă și care au obținut la testul grilă notă mai mică sau egală ca cea de la testul practic.

Puteți folosi ca intrare fie fișierul students.txt fie fișierul students.csv.

Output-ul scriptului va conține 3 linii de forma:

MUȘATESCU V. Alexandru-Petrișor,311CC,8.5,9
NEACȘU C. Florin-Mărgărit,314CC,9,9
ȘTIRBĂȚ A. Steliana,315CC,10,10

4. Sortare studenți în grupă (1 karma point)

Ne propunem să afișăm studenții unei grupe, sortați după nota finală (coloana a 3-a).

Creați un script numit sort-students-by-group care primește ca argument un șir reprezentând grupa și afișează numele și nota finală, separate prin virgulă (,, comma), a studenților din acea grupă sortați după nota finală. Realizați sortare inversă (reverse) astfel încât cele mai mari note să fie primele.

Nota finală, după care se face sortarea, este o valoare numerică.

Scriptul trebuie să afișeze un mesaj de eroare în cazul în care nu primește exact un argument sau în cazul în care argumentul nu reprezintă o grupă validă. Grupe valide sunt 311CC, 312CC, 313CC, 314CC, 315CC.

Puteți valida parametrul comparând pe rând cu fiecare dintre cele 5 grupe valide. Sau puteți folosi expresii regulate printr-o construcție de forma

echo "$1" | grep '...' > /dev/null
if test $? -eq 0; then   # if group was matched
    ...

Puteți folosi ca intrare fie fișierul students.txt fie fișierul students.csv.

5. Sortare grupe după note de 10 obținute de studenți (2 karma points)

Ne propunem să afișăm grupele sortate în funcție de câți studenți din acea grupă au obținut nota 10.

Creați un script numit sort-groups-by-grade care afișează fiecare grupă și numărul de note de 10 obținute de studenții din acea grupă, separate prin virgulă (,, comma), sortate după numărul de note de 10. Sortarea să fie inversă, adică grupele cu cele mai multe note de 10 să fie primele.

Click pentru indicații

Click pentru indicații

În Bash puteți folosi array-uri asociative, adică array-uri pentru care indexul poate fi un șir, nu neapărat un număr.

Un mod simplu prin care putem să inițializăm și să afișăm un array asociativ este descris mai jos, împreună cu un exemplu de rulare. Puteți pleca de la acest exemplu în rezolvarea acestui exercițiu. Scriptul sample-bash-associative-array se găsește în directorul laboratorului în subdirectorul process-table-data/.

student@mjolnir:~/uso.git/lab11/process-table-data$ cat sample-bash-associative-array 
#!/bin/bash

# Initialize grades array.
declare -A grades
for group in "311CC" "312CC" "313CC" "314CC" "315CC"; do
    grades["$group"]=2
done

# Print groups and grades.
for group in "311CC" "312CC" "313CC" "314CC" "315CC"; do
    echo "$group,${grades[$group]}"
done
student@mjolnir:~/uso.git/lab11/process-table-data$ ./sample-bash-associative-array 
311CC,2
312CC,2
313CC,2
314CC,2
315CC,2

Pentru informații detaliate despre array-uri în shell aveți două posibilități:

  1. Cautați în pagina de manual a bash șirul Arrays și localizați secțiunea corespunzătoare.
  2. Căutați pe Google după șirul bash arrays. Puteți da click pe link.

Acest exercițiu poate fi rezolvat și cu un one liner. Dacă folosiți corespunzător filtre de text precum cut, tr, sed, awk, grep, sort, uniq, veți putea obține aceeași soluție, fără să folosiți array-uri asociative.

Puteți folosi ca intrare fie fișierul students.txt fie fișierul students.csv.

În cazul unei implementări corecte, în urma rulării scriptului veți obține output-ul

314CC,4
311CC,3
315CC,3
313CC,2
312CC,1

Grupa 314CC este prima grupă afișată întrucât are cel mai mare număr de studenți care au obținut nota 10: 4 studenți. Grupa 312CC este ultima grupă afișată întrucât are cel mai mic număr de studenți care au obținut nota 10: 1 student.

Sumar

Pe parcursul acestui laborator am acoperit câteva aspecte practice legate de expresii regulate si shell scripting.

Expresiile regulate reprezintă șiruri de caractere în care unele caractere au rol special și pot ține locul altor caractere. Spunem că o expresie regulată se potrivește sau face match pe un șir dat (sau pe mai multe șiruri). Exemple de caractere speciale sunt:

  • ^ (început de rând)
  • $ (sfârșit de rând)
  • * (expresia anterioară de oricâte ori, posibil niciodată)
  • + (expresia anterioară de oricâte ori, cel puțin o dată)
  • . (orice caracter)

Expresiile regulate sunt folosite pentru căutare, selecție și verificare în secțiuni text.

Expresiile regulate sunt întâlnite în toate limbajele de programare. În shell, utilitare precum grep, sed, awk folosesc expresii regulate. grep primește ca argument o expresie regulată și un fișier sau set de fișiere unde face căutarea; grep va afișa liniile care conțin șiruri pe care face match expresia regulată primită ca argument.

Putem manipula diverse data folosind utilitarele tr, sed, cut, awk, sort.

Utilitarul tr permite translatarea, ștergerea și manipularea caracterelor primite la intrare. Sed este un stream editor ce poate efectua transformări la nivel de string asupra unui text primit la intrare. În plus, sed poate primi expresii regulate ca argument de căutare.

Utilitarul cut permite extragerea informațiilor structurate pe linii și coloane. Utilitarul awk permite aceleași acțiuni ca și cut, dar funcționalitatea sa este mai extinsă. O diferența intre cut și awk este că awk poate folosi o expresie regulată ca delimitator, pe când cut acceptă un singur caracter.

În shell, pentru sortarea datelor putem folosi comanda sort. Comanda sort două opțiuni importante:

  • -t care precizează separatorul de câmpuri (field separator)
  • -k care precizează cheia/cheile după care se face sortarea. De obicei opțiunea va fi folosită, de exemplu, în forma -k 2,2, însemnând că sortarea se face după a doua cheie.

Un script shell este un fișier text care conține comenzi și construcții specifice shell-ului. Un script contine comenzi uzuale folosite în shell, dar si construcții specifice precum while, if, for.

Un script moștenește variabilele de mediu ale shell-ului părinte precum HOME, BASH, IFS, USER. Pe lângă aceste variabile, scriptul poate primi o serie de argumente din linia de comandă pe care le putem folosi cu ajutorul: $*, $@, $1, $2 … $n, $0,$#.

Pentru parsarea datelor tabelare în scriptuti, putem folosi construcția while read ... în conjuncție cu variabila internă IFS (Input Field Separator). Cu ajutorul construcției putem citi linie cu linie și apoi parsa câmpurile fiecărei linii și stoca în variabile individuale. Apoi putem face operații pe acele câmpuri.

Pentru verificarea diverselor conditii in scripturi putem folosi constructia if test ....

uso-ac/laboratoare/laborator-10.txt · Last modified: 2016/12/10 23:18 by giorgiana.vlasceanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0