This is an old revision of the document!
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
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
În continuare, împreună cu asistentul veți parcurge câteva exerciții de tip demo/tutorial.
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ă |
Construcție | RegExp | Glob |
. | orice caracter | caracterul . |
.* | orice caracter de oricâte ori, posibil niciodată | orice incepe cu . |
? | expresia anterioară o dată sau niciodată | orice caracter |
a+ | caracterul a de oricâte ori, cel puțin o dată | caracterul a urmat de + |
Î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
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.
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:
-
);F
(majusculă)[a-z]
)+
)).
, 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ă.
+
, 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
[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]
.
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.
Spre exemplu, folosind 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 [...]
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
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 intrarilen
sortare numerică (implicit este alfabetica)-r
sortare inversa (implicit crescator)
-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
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. Dacă nu este specificat nici un shell prin shebang atunci implicit va fi luat shell-ul implicit (setat în /etc/passwd
) asignat utilizatorului logat.
Spre exemplu, one-liners pe care le-am tot scris până acum ar putea fi puse într-un shell script și, mai mult, dacă sunt comenzi lungi și nu încap pe o linie le putem “sparge” în mai multe linii folosind separatorul \
:
#!/bin/bash export DATE=$(date +20%y%m%d) && \ mkdir -p /usr/share/snapshots && \ tar -cvpzf /usr/share/snapshots/$HOSTNAME_$DATE.tar.gz \ --exclude=/proc --exclude=/lost+found \ --exclude=/sys --exclude=/mnt \ --exclude=/media --exclude=/dev \ --exclude=/share/Archive /
\
trebuie să nu mai existe nici un caracter (nici măcar spațiu alb/trailing whitespaces)
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ă.
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 4 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
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=','
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:
cut
permitea afișarea de coloane doar în ordinea din fișierul de intrare;while read
informația parsată. Spre exemplu, afișarea poate avea forma "Studentul ... face parte din grupa ...".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
În bash există și construcții de tipul for
. Un exemplu comun este acela de a itera prin conținutul unui director și de 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
Se poate itera și pe output-ul unei comenzi:
#!/bin/bash cd ~ cd uso/lab10 unzip media.zip cd media for file in $(find . -iname '*.jpg') do echo $file done
student@mjolnir:~/uso/lab10$ vim myScript.sh student@mjolnir:~/uso/lab10$ chmod +x myScript.sh
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.
/usr/include/
.
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
.
Comanda va avea ca output liniile ce conțin definiții de macro-uri în cadrul fișierului /usr/include/stdio.h
.
Î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/
.
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.
Î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.
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).
Î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.
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.
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/vim80/tutor/tutor
:
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:
Creați expresia regulată care face match pe șiruri de 5 litere mici sau mari.
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.
Rulați comanda grep
cu expresia regulată actualizată peste fișierul /usr/share/vim/vim80/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.
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.
Rulați comanda grep
cu expresia regulată actualizată peste fișierul /usr/share/vim/vim80/tutor/tutor
pentru a extrage liniile ce conțin cuvinte de 5 litere mici sau mari.
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.
grep
.
Ca să identificați operatorul de repetiție pentru expresii regulate în grep
, aveți următoarele posibilități.
grep
șirul Repetition și localizați opțiunea necesară.
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 (+
).
Î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:
students.txt
în locul fișierului students.csv
.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
.
Î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.
./extract-name-tab
Ca să verificați că vă afișează toată informația numărați liniile afișate; rezultatul trebuie să fie 92
:
$ ./extract-name-tab | wc -l 92
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ă.
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
.
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
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.
Va trebui să actualizați scriptul extract-sort-grades
în două locuri:
6
și 9
(în loc de număr egal cu 10
cum este cazul în scriptul extract-name-tab
). Vedeți indicațiile.extract-name-tab
).
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
.
Î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.
Î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ă.
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ă:
1
și 10
.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
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"
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
.
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ă.
Folosiți comanda identify
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.
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.
echo "$1" | grep <suitable-regex> > /dev/null 2>&1
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
.
Î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.
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
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.
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
.
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
.
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.
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.
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 ...
.