Laboratorul 2: Căutare, liste de comenzi, compilare¶
Căutarea¶
Căutarea unui fragment de text într-un fișier se poate face folosind expresii regulate în cadrul comenzii grep(1). Fie următorul fișier text:
unde \n reprezintă o linie nouă (ca în limbajul C). Pentru a găsi cuvintele ce conțin at în fișier folosim comanda:
Dacă vrem să găsim cuvintele care se termină în litera t atunci vom folosi comanda:
unde caracterul $ simbolizează sfârșitul liniei. Pentru începutul liniei se folosește ^:
Alte caractere speciale utile, numite wildcards, sunt:
*– găsește de 0 sau mai multe ori atomul precedent+– găsește de 1 sau mai multe ori atomul precedent?– găsește de 0 sau 1 ori atomul precedent
unde un atom este implicit caracterul precedent sau grupul precedent de caractere. Gruparea mai multor caractere se face cu ajutorul parantezelor rotunde (). În exemplul nostru putem căuta toate cuvintele care încep cu rata și sunt urmate de ta de 0 sau mai multe ori astfel:
$ grep -E 'rata(ta)*' rat
rata
ratat
ratata
rata tata
$ grep -E 'rata(ta)+' rat
ratata
$ grep -E 'rata(ta)?' rat
rata
ratat
ratata
rata tata
Observați că am folosit parantezele pentru grupare și opțiunea -E pentru a folosi expresii regulate.
În Ubuntu, grep(1) colorează partea din fiecare linie care se potrivește cu expresia, așa că puteți vedea exact ce a găsit:

Liste de comenzi¶
Așa cum s-a menționat la curs, o listă de comenzi care se execută secvențial poate fi tipărită pe o singură linie la promptul shell de maniera următoare:
cmd1; cmd2; cmd3; ...– se execută mai întâicmd1, după ce se terminăcmd1se executăcmd2, ș.a.m.d.
Iată un exemplu concret:
În comanda de mai sus (o listă de comenzi este până la urmă și ea o comandă), echo -n suprimă afișarea caracterului newline.
Expresii logice¶
Toate shell-urile POSIX oferă acces la expresii logice pentru a procesa și lega diferite comenzi. Expresiile logice primare de tip și/sau au aceeași sintaxă ca în limbajul C: &&, respectiv ||.
Comenzile efectuate în shell se execută cu succes sau nu și întorc o valoare pentru a semnala acest lucru. Valoarea întoarsă și semnificația ei este documentată în manual. În general o valoare nulă semnifică succes și una nenulă eșec. Din această cauză modul de operare al expresiilor logice este pe dos:
cmd1 && cmd2– executăcmd2doar dacă ieșirea luicmd1este zero;cmd1 || cmd2– executăcmd2doar dacă ieșirea luicmd1este nenulă.
Valoarea întoarsă de ultima comandă executată este salvată în variabila $?. În următorul exemplu, întâi creăm un fișier nou animals.txt în care punem cuvântul cat, după care căutăm pe rând cuvintele cat și dog în acest fișier. Prima dată căutarea se întoarce cu succes (zero), iar a doua oară fără niciun rezultat deci cu valoare nenulă (unu).
În funcție de ieșire se iau diferite decizii. De exemplu, fie fișierul agenda.txt în care se găsesc datele de contact ale diferitor persoane. Pentru a căuta dacă o anumită persoană există în agendă se poate folosi comanda grep(1):
La finalul execuției comenzii grep(1) aceasta iese fie cu valoarea 0, dacă s-au găsit una sau mai multe intrări, sau 1 dacă nu s-a găsit nimic. Pentru a obține un verdict mai prietenos putem folosi expresia logică sau:
$ echo Ana > agenda.txt
$ grep Ana agenda.txt || echo "Persoana nu exista!"
Ana
$ grep Mara agenda.txt || echo "Persoana nu exista!"
Persoana nu exista!
Dacă nu ne amintim dacă am trecut-o pe Ana-Maria drept Ana sau Maria în agendă, putem de asemenea folosi expresii logice pentru a căuta după Maria în caz că Ana nu există:
Compilarea programelor C¶
Compilarea și executarea unui program C¶
În sistemele de operare de tip UNIX, compilatorul de C este invocat prin comanda cc(1) (scurt de la C compiler). Acesta așteaptă ca argumente fișierele sursă și, opțional, numele executabilului rezultat în urma compilării. În Linux, compilatorul uzual de C este varianta GNU, iar compilatorul se numește gcc.
În comanda de sus, compilatorul primește fișierul C hello.c și numele executabilului hello transmis prin opțiunea -o (de la output). Atenție, dacă omiteți specificarea unui nume pentru executabil acesta va fi implicit denumit a.out din motive istorice.
Pentru a executa binarul rezultat se folosește comanda:
unde hello este numele executabilului, iar ./ spune shell-ului să nu caute executabilul în $PATH pentru că se află în directorul curent.

Formatul cel mai general al funcției main conține în semnătura funcției numărul argumentelor din linia de comandă (argc), un vector de stringuri care conține argumentele propriu-zise din linia de comandă (argv), și respectiv mediul de execuție reprezentat ca un vector de stringuri (envp). Fiecare element al vectorului care reprezintă mediul de execuție are forma discutată nume=valoare. Aceasta este modalitatea prin care shell-ul comunică mediul de execuție comenzilor pe care le lansează și care astfel pot folosi valorile variabilelor de mediu pentru a-și adapta comportamentul la mediul de execuție.
Compilați și executați similar cu programul hello.c de mai sus programul printenv.c de mai jos, care tipărește prima variabilă de mediu din listă.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *envp[])
{
printf("Prima variabila de mediu este %s\n", envp[0]);
exit(0);
}
Obs: Așa cum am menționat anterior, fiecare comandă (program executabil) Unix întoarce un cod de retur folosit așa cum am văzut în comenzi condiționale, de pildă. Convenția Unix este că orice program care întoarce cod de retur 0 s-a terminat cu succes, în vreme ce un cod nenul (uzual pozitiv) desemnează o condiție de eroare. Din acest motiv, este o bună practică de programare să încheiați programele folosind apelul exit care este responsabil pentru a comunica acest cod de retur, care e returnat de către shell prin evaluarea variabilei $?.
Sarcini de laborator¶
-
Utilitarul
wc(1), cu opțiunea-lva afișa numărul de linii al fișierului (sau fișierelor) primit ca argument.Să presupunem că am vrea să efectuăm această operație pentru toate fișierele cu o anumită proprietate. Încercăm acest lucru în două moduri, dintre care doar al doilea funcționează:
$ find . -name "*.tex" | wc -l 3 $ find . -name "*.tex" | xargs wc -l 300 ./Curs1/curs1.tex 255 ./Curs2/curs2.tex 84 ./Curs0/curs0.tex 639 totalCitiți documentația pentru
xargs(1) și încercați să explicați comportamentul diferit al celor două comenzi de mai sus. -
Creați un director
logsși câteva fișiere în acesta. În unele (nu toate) dintre acestea, adăugați textulerror. Folosindgrep(1) șirm, ștergeți toate fișierele care conțin textulerror. -
Creați două directoare
origșibackup. În directorulorigcreați fișierelefoo,bar,baz. Folosiți comandafind(1) să copiați toate fișierele dinorigînbackupdar cu extensia.origîn plus (ex.foo.orig). -
Modificați programul
hellosă citească un nume cuscanf(ex.Alex) pe care să-l salute pe urmă cu ajutorul funcțieiprintf(ex."Hello, Alex!"). Compilați și executați. Încercați să combinați programul vostru cu un pipe. -
Modificați programul
printenvde mai sus astfel încât să tipărească toate variabilele de mediu (N.B. vectorulenvpse termină cuNULL). Compilați și executați. -
Creați un director
binîn care copiați executabilulhello. Adăugați directorulbin(folosind calea absolută) în variabila$PATHși arătați că puteți executa simplu cu comandahellofără a avea nevoie de prefixul./.