Sunteți pe pagina 1din 12

Iat cteva posibile rezolvri pentru exerci iile din al cincilea laborator: <!

----------------------------------------------------------------------------------------------------------------------------------------------------->

Ex.1 S se scrie un script care calculeaz i afieaz numrul total de fiiere de tip obin ce sunt "ascunse" (i.e. numele lor ncepe cu caracterul '.'), numrul total de fiiere de tip alias (i.e. link-uri hard sau soft) i numrul tot al de subdirectoare aflate ntr-un director dat, parcurgand acel director n manier recursiv. Numele directorului de pornire se transmite ca parametru n linia de comand, sau se va citi prin comanda read n caz contrar. (Indica ie: parcurge i cu un for directorul curent i calcula i informa iile solicit ate, iar pentru fiecare intrare din director care este de tipul (sub)director apela i recursiv scriptul.) Suplimentar: s se afieze acele informa ii totale pentru fiecare subdirector pa rcurs.

Ideea de rezolvare: - recursia se poate implementa n dou maniere diferite: fie prin apel recursiv de f unc ie, fie prin apel recursiv de script; - statisticile (i.e. valorile ce se cer a fi calculate n enun ul problemei) se calc uleaz ntr-o manier asemntoare cu cea din rezolvrile date mai jos la exerci iul 4 (not: citi i cu aten ie i observa ia de la finalul rezolvrilor date pentru exerci iul 4). <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.2 S se scrie un script care calculeaz numrul total de cuvinte i respectiv lungim ea cea mai mare a liniilor de text, din toate fiierele ce con in text obinuit (i.e. fiierele pentru care comanda file raporteaz "ASCII text") aflate ntr-un director dat, parcurgand acel director n manier recursiv. Numele directorului se transmite ca parametru n linia de comand, sau se va c iti prin comanda read n caz contrar. (Indica ie: parcurge i cu un for directorul curent i calcula i informa iile solicit ate, iar pentru fiecare intrare din director care este de tipul (sub)director apela i recursiv scriptul.) Suplimentar: s se afieze acele informa ii totale pentru fiecare subdirector pa rcurs. Ideea de rezolvare: - similar ca la exerci iul 1, recursia se poate implementa n dou maniere diferite: f ie prin apel recursiv de func ie, fie prin apel recursiv de script; - statisticile (i.e. valorile ce se cer a fi calculate n enun ul problemei) se calc uleaz ntr-o manier asemntoare cu cea din rezolvarea dat mai jos la exerci iul 5. <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.3 S se scrie un script care s efectueze ntr-o bucl paii urmtori: 1) citete de la tastatur dou numere i un operator + , - , * sau / 2) efectueaz opera ia respectiv 3) scrie rezultatul, pe o nou linie, n cadrul unui fiier, sub forma: nr_ope ratie: operand1 operator operand2 = rezultat 4) reia bucla Din bucla respectiv se va iei la introducerea caracterului q pe pozi ia operat

orului. nainte de terminare, se va scrie n fiier i numrul total de opera ii efectuate. Numele fiierului n care se face scrierea se primete ca parametru n linia de co mand, sau se va citi prin comanda read n caz contrar. lab5_ex3.sh : ============= #!/bin/bash ### calculator aritmetic if [ $# -eq 1 ] ; then fisier=$1 else echo -n "Dati numele fisierului: " ; read fisier fi nr_op=0 opd="start" echo >$fisier until [ "$opd" = "q" ] do echo -n "Dati primul numar: " ; read op1 echo -n "Dati al doilea numar: " ; read op2 echo -n "Dati operatorul: " ; read opd let nr_op++ case "$opd" in "+" ) let rez=op1+op2 ;; "-" ) let rez=op1-op2 ;; "*" ) let rez=op1*op2 ;; "/" ) rez=`echo "scale=5; $op1/$op2" | bc -l` ;; esac echo "$nr_op: $op1 $opd $op2 = $rez" >>$fisier

done

<!-----------------------------------------------------------------------------------------------------------------------------------------------------> Exerci ii suplimentare: <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Rezolvri pentru cteva dintre exerci iile suplimentare. <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.4 S se scrie un script care calculeaz i afieaz numrul total de fiiere de tip fifo numrul total de subdirectoare aflate ntr-un director dat, parcurgand directorul recursiv. Numele directorului de pornire se transmite ca parametru n linia de comand, sau se va citi prin comanda read n caz contrar. (Indica ie: parcurge i cu un for directorul curent i calcula i informa iile solicit ate, iar pentru fiecare intrare din director care este de tipul (sub)director apela i recursiv scriptul.) Suplimentar: s se afieze acele informa ii totale pentru fiecare subdirector pa rcurs.

a) Prima solu ie: folosim o func ie recursiv pentru a parcurge subarborele de fiiere c u rdcina n directorul dat. lab5_ex4a.sh : ============== #!/bin/bash ### calculeaza recursiv numarul de fisiere fifo si subdirectoare dintr-un direct or dat ### afiseaza numarul total de fisiere fifo si subdirectoare continute de acesta if [ $# -eq 0 ] then echo -n "Dati numele directorului de start: " read numedir else numedir=$1 fi nr_fif=0 nr_dir=0 function parcurgere_director () { for f in `ls -A $1` do cale=$1/$f if [ -d $cale ] ; then let nr_dir++ parcurgere_director $cale elif [ -p $cale ] ; then let nr_fif++ fi done } parcurgere_director $numedir

# apelul recursiv

# apelul initial

echo "Directorul $numedir contine $nr_dir directoare si $nr_fif fisiere fifo." b) A doua solu ie: n loc de func ie recursiv, folosim apelul recursiv al scriptului.

Observa i forma de apel a scriptului - este prima dintre formele de apel a unei co menzi simple, prin care se creeaz un nou proces shell (i.e. interpretorul bash) ce ruleaz o nou instan a scriptului. Ca atare, ntruct variabilele nr_dir i nr_fif sunt locale fiecrui proces shell n parte, nu mai putem doar s le incrementm ca la solu ia anterioar cu func ie apelat rec rsiv (unde toate apelurile sunt executate n cadrul unui singur proces shell, nu se creeaz cte un nou proces pentru fiecare ape l de func ie). Din acest motiv, pentru a putea transmite totalurile intermediare din apelurile recursive ale scriptului, putem folosi scrierea lor pe ecran i preluarea n procesul apelant cu substitu ia special comand (Observa ie: nu merge nici s folosim codul de terminare n acest scop, deoarece sunt 2 valori ce trebuie transmise procesului apelant, plus c avem i limi tarea de 1 octet pentru reprezentarea codului de terminare). lab5_ex4b.sh : ==============

#!/bin/bash ### calculeaza recursiv numarul de fisiere fifo si subdirectoare dintr-un direct or dat ### afiseaza numarul total de fisiere fifo si subdirectoare continute de acesta if [ $# -eq 0 ] then echo -n "Dati numele directorului de start: " read numedir else numedir=$1 fi nr_fif=0 nr_dir=0 for f in `ls -A $numedir` do cale=$numedir/$f if [ -d $cale ] ; then let nr_dir++ results=` $0 $cale apel_recursiv ` # apelul recursiv (intr-un n ou proces bash) si preluarea celor 2 totaluri intermediare let nr_dir+=` echo $results | cut -f1 -d: ` let nr_fif+=` echo $results | cut -f2 -d: ` elif [ -p $cale ] ; then let nr_fif++ fi done if test M$2 == Mapel_recursiv then # apelurile recursive echo "$nr_dir:$nr_fif" # aici scriem pe ecran cele doua valori numerice ( ele vor fi preluate in procesul apelant prin substitutia `...`) else # doar apelul initial echo "Directorul $numedir contine $nr_dir subdirectoare si $nr_fif fisiere fif o." fi

Not: observa i folosirea M-ului n ambii termeni din ultimul test; el are doar un rol ajuttor, pentru ca test-ul s nu dea eroare de sintax datorit faptului c $2 este irul vid (ceea ce se ntmpl la apelul ini ial al scriptului). Doar apelurile recursive vor primi un al doilea parametru, marcatorul 'apel_recursiv', pentru a putea afia rezultatele n format di ferit la apelurile recursive fa de apelul ini ial. Observa ie: aceast solu ie are un dezavantaj - fiecare apel recursiv produce un nou p roces shell i, ca urmare, dac apela i scriptul avnd ca argument un director pentru care subarborele de fiiere cu rdcina n el are o adncime d e aprox. 25 de nivele, v ve i atinge limita maxim de procese simultane per utilizator fixat pe serverul fenrir. n continuare vom vedea cum putem nltura acest dezavantaj: vom folosi pentru apelul recursiv a treia form de apel a unei comenzi simple, valabil numai pentru scripturi (i anume, forma cu cuvintele cheie . sau source: s ource script [parametri] ), prin care nu se creeaz un nou proces shell (i.e. interpretorul bash) pentru a rula noua instan a scriptului, ci tot procesul shell curent o va executa.

n acest fel, pe lng posibilitatea de transmitere a rezultatelor intermediare prin s crierea lor pe ecran i recuperarea cu substitu ia special comand (cum am procedat la solu ia anterioar), avem o nou posibilitate: folosirea uno r variabile la fel ca la prima solu ie, cea cu func ia recursiv, cci de data aceasta avem un singur proces shell pe toat durata de execu ie a tuturor apelurilor scriptului. c) A treia solu ie: folosim apelul recursiv al scriptului cu forma de apelare sour ce i variabile n care contorizm totalurile. Pentru a diferen ia apelul ini ial de apelurile recursive ale scriptului, vom aduga l a apelurile recursive un marcator 'apel_recursiv' drept al doilea parametru doar la apelurile recursive. Este necesar s facem o astfel de diferen iere nu doar pentru a afia rezultatele totale doar n apelul ini ial (i nu i n cele recursive), dar i pentru a ini ializa cu zero o singur la nceput, cele dou variabile n care contorizm numrul de subdirectoare i de fiiere (similar ca la prima solu ie, cea cu fun c ie recursiv). lab5_ex4c.sh : ============== #!/bin/bash ### calculeaza recursiv numarul de fisiere fifo si subdirectoare dintr-un direct or dat ### afiseaza numarul total de fisiere fifo si subdirectoare continute de acesta case $# in 0 ) echo -n "Dati numele directorului de start: " read cale ;; 1 ) cale=$1 ;; 2 ) if test $2 != "apel_recursiv" ; then echo "Something is wrong with the parameter(s) for the script..." 1>&2 ; exit 1 fi cale=$1 esac if test $# -lt 2 # doar apelul initial then if test ! -d $cale ; then echo "Eroare: numele dat nu exista ca director." 1>&2 ; exit 2 fi nr_fif=0 # initializarea acestor variabile trebuie facuta doar la primul ap el initial al scriptului nr_dir=0 fi for fis in `ls -A $cale` do if test -d $cale/$fis ; then let nr_dir++ source $0 $cale/$fis apel_recursiv urce ...

# apelul recursiv in forma so

# la intoarcerea din recursie trebuie refacuta calea dinaintea apelului (caci e un singur proces shell, # si ca atare variabila cale va fi modificata in timpul apelurilor recur sive) # putem folosi comanda dirname pentru a reface valoarea corecta pentru v

ariabila cale cale=`dirname $cale` # dirname practic va taia din variabila cal e sufixul /$fis ce-i fusese adaugat in urma apelului recursiv elif test -p $cale/$fis ; then let nr_fif++ fi

done

if test $# -lt 2 # facem afisarea doar la sfarsitul calculului, in apelul ini tial al scriptului then echo "Directorul $cale contine $nr_dir directoare si $nr_fif fisiere fifo." fi

d) A patra solu ie: similar cu cea de-a treia solu ie, i.e. folosim apelul recursiv a l scriptului cu forma de apelare source i variabile n care contorizm totalurile, precum i un marcator pentru a diferen ia apelul ini ial de apelu rile recursive ale scriptului, lucru necesar din aceleai motive ca la solu ia a treia. Diferen a fa de solu ia a treia const n faptul c vom elimina necesitatea de a gestiona a gumentul cale curent (i.e. variabila cale de mai sus), prin renun area pur i simplu la acest parametru transmis scriptului. n locul lui vom folosi alt idee: schimbarea directorului curent de lucru naintea apelului recursiv i refacerea sa dup revenirea din apelul recursiv. lab5_ex4d.sh : ============== #!/bin/bash ### calculeaza recursiv numarul de fisiere fifo si subdirectoare dintr-un direct or dat ### afiseaza numarul total de fisiere fifo si subdirectoare continute de acesta case $# in 0 ) echo -n "Dati numele directorului de start: " read director_initial ;; 1 ) director_initial=$1 ;; 2 ) if test $1 != "apel" -o $2 != "recursiv" ; then echo "Something is wrong with the parameter(s) for the script..." 1>&2 ; exit 1 fi esac if test $# -lt 2 # doar apelul initial then if test ! -d $director_initial ; then echo "Eroare: numele dat nu exista ca director." 1>&2 ; exit 2 fi initial_wd=`pwd` # salvam valoarea initiala a directorului curent de lucr u (pentru a o reface la final) cd $director_initial # schimbam directorul curent de lucru in directorul dat pentru calculele cerute nr_fif=0 # initializarea acestor variabile trebuie facuta doar la apelul initial al scriptului nr_dir=0 fi

for fis in `ls -A` do if test -d $fis ; then let nr_dir++ cd $fis source ~/lab5_ex1d.sh apel recursiv # apelul recursiv : numele script ului trebuie specificat prin cale absoluta (sau relativa la directorul home) cd .. # (nu prin cale relativa la direc torul curent de lucru, caci acesta se modifica de la un apel la altul) elif test -p $fis ; then let nr_fif++ fi done if test $# -lt 2 # facem afisarea doar la sfarsitul calculului, in apelul initial al scriptului then echo "Directorul $director_initial contine $nr_dir directoare si $nr_fif fis iere fifo." cd $initial_wd # refacem valoarea initiala a directorului curent de l ucru fi Not: de data aceasta am folosit marcatorul apel recursiv n loc de apel_recursiv pe ntru a avea 2 cuvinte, primul cuvnt (apel) va fi primul argument pentru apelurile recursive (cci nu mai avem o cale dat ca argument n acest e cazuri), iar al doilea cuvnt (recursiv) pentru a avea 2 argumente la apelurile recursive. Se poate folosi i un singur cuvnt ca marcator (i.e. apel_recursiv), dar atunci trebuie modificate n mod corespunztor case-ul i cele 2 if-uri folosite pentru a diferen ia apelul ini ial de ap elurile recursive ale scriptului.

Observa ie comun pentru toate solu iile de mai sus: n rezultatele ob inute nu vor fi luate n calcul fiierele fifo sau subdirectoarele car e con in n nume spa ii sau tab-uri, datorit modului de evaluare a enumerrii din structura "for variabila in lista-de-cuvinte" ceea ce fa ce imposibil protejarea spa iilor din numele fiierelor i subdirectoarelor ob inute cu comanda ls. Repararea acestui neajuns se poate face folosind o singur comand find, care implic it va parcurge recursiv directorul dat, i cu op iunea -exec cmd i se poate da o comand cmd adecvat care s fac actualizarea variabilelor de contoriza re.

ns aceast solu ie nu se ncadreaz n scopul acestui exerci iu, acela de a scrie dvs. un s pt/o func ie care s parcurg recursiv prin apeluri explicite (!) directorul dat. Ca atare, ignorm neajunsul amintit pe care-l au sol u iile propuse mai sus. <!----------------------------------------------------------------------------------------------------------------------------------------------------->

Ex.5 S se scrie un script care calculeaz numrul total de linii de text i respectiv de caractere din toate fiierele dintr-un director, prin parcurgerea recursiv a directorului dat. Numele directorului se transmite ca parametru n linia de comand, sau se va c iti prin comanda read n caz contrar. (Indica ie: parcurge i cu un for directorul curent i calcula i informa iile solicit ate, iar pentru fiecare intrare din director care este de tipul (sub)director apela i recursiv scriptul.) Suplimentar: s se afieze acele informa ii totale pentru fiecare subdirector pa rcurs. Exerci iul se rezolv similar cu cel precedent, parcurgerea recursiv a subarborelui d e fiiere se poate face prin oricare dintre cele 4 metode ilustrate mai sus, doar statisticile calculate sunt diferite. Vom ilustra n continuare doar prima metod de rezolvare de la exerci iul 4, adaptat pe ntru calculele cerute la acest exerci iu. lab5_ex5.sh : ============= #!/bin/bash ### calculeaza recursiv numarul de linii si numarul de caractere din toate fisie rele normale dintr-un director dat si din toate subdirectoarele sale ### afiseaza numarul total de linii si numarul total de caractere calculate if [ $# -eq 0 ] then echo -n "Dati numele directorului de start: " read numedir else numedir=$1 fi nr_linii=0 nr_chars=0 function parcurgere_director () { for f in `ls -A $1` do cale=$1/$f if [ -d $cale ] ; then parcurgere_director $cale # apelul recursiv elif [ -f $cale ] ; then rezl=`wc -l $cale | cut -d" " -f1` #sau, echivalent: rezl=`cat $ca le | wc -l` rezc=`wc -c $cale | cut -d" " -f1` #sau, echivalent: rezc=`cat $ca le | wc -c` let nr_linii+=rezl let nr_chars+=rezc

done

fi

parcurgere_director $numedir

# apelul initial

echo "Directorul $numedir contine $nr_linii linii de text si $nr_chars caractere in fisierele aflate in el sau in subirectoarele sale."

Observa ie: se poate rafina enun ul problemei, n sensul de a lua n calcul doar fiierele text, nu i cele binare. n acest caz, se poate folosi comanda file pentru a depista care dintre fiiere sunt binare i respectiv care sunt fiiere text. <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.6 S se scrie un script care s afieze un arbore cu structura directoarelor din c ontul personal. lab5_ex6.sh : ============= #!/bin/bash ## afiseaza un arbore cu structura directorului curent de lucru al utilizatorulu i, sau al altui director specificat ca argument function afisare_indentata() { # functia primeste doua argumente: primul este un nume de director si al doilea este nivelul curent in arbore (folosit # pentru a crea textul de indentare utilizat la afisarea arborelui) for f in `ls -A $1` # folosim optiunea -A in loc de -a pentru a nu afisa si intrarile implicite . si .. do cale=$1/$f let nivel=$2+1 sir_indentare= for i in `seq $nivel` do sir_indentare=$sir_indentare"-> " done echo -e $sir_indentare$cale if [ -d $cale ] # daca-i director, apelam recursiv then afisare_indentata $cale $nivel fi

done

if test $# -eq 0 ; then numedir=`pwd` ; else numedir=$1 ; fi echo $numedir afisare_indentata $numedir 0 <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.7 S se scrie un script care s afieze toate comentariile din scripturile aflate n tr-un director specificat ca argument (scripturile vor avea o extensie .sh pentru a le putea deosebi de alte genuri de fiiere text). lab5_ex7.sh : =============

#!/bin/bash lista=`find $1 -name "*.sh"` for f in $lista do echo $f grep -n '#' $f | grep -v '$#' | grep -v '#!' done Observa ie: ultimele dou comenzi grep au rolul de a elimina liniile n care apare var iabila special $#, respectiv de a elimina linia de forma #!shellul_dorit (aflat de obicei la nceputul script-urilor). Evident, ast fel se vor pierde la afiare eventualele comentarii scrise pe aceeai linie n care apare $# sau #!shell. <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.8 S se scrie un script care primete ca parametri d (un nume de director) i n (u n numr). Pentru fiecare fiier pentru care utilizatorul curent are drepturi de citire i execu ie, aflat n directorul dat ca parametru sau n su bdirectoarele acestuia, s se afieze primele n linii de text. lab5_ex8.sh : ============= #!/bin/bash if [ $# -eq 2 ] then if [ -d $1 ] then for f in `find $1 -type f` # parcurgerea recursiva o obtinem aici cu comanda find do if [ -r $f -a -x $f ] then echo Fisierul $f : primele $2 linii de text sunt urmatoarele: head -n $2 $f fi done else echo "Primul argument trebuie sa fie director" exit 2 fi else echo "Utilizare: $0 director numar" exit 1 fi <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.9 S se scrie un script care primete ca parametru un nume de director. Scriptul va compila fiecare fiier surs C din directorul respectiv i va tipri (i.e., afia pe ecran) con inutul fiecrui fiier text din acest director. lab5_ex9.sh : =============

#!/bin/bash function comp() { out=`basename $1 .c` gcc $1 -o $out } function afis() { cat $1 } if [ $# -eq 0 ] then echo "Utilizare: $0 director" exit 1 else if [ ! -d $1 ] then echo "Primul argument trebuie sa fie director!" exit 2 else for f in `ls -A $1` do case $f in *.c ) echo "fisier sursa C: $f" ; comp $f ;; *.txt ) echo "fisier text: $f" ; afis $f ;; esac done fi fi <!-----------------------------------------------------------------------------------------------------------------------------------------------------> Ex.10-13 ncerca i s le rezolva i singuri. <!----------------------------------------------------------------------------------------------------------------------------------------------------->

<!-----------------------------------------------------------------------------------------------------------------------------------------------------> II) Script pentru automatizarea procesului de dezvoltare de programe C : S se scrie un script care s v ajute la scrierea programelor n C, prin care s se automatizeze ciclul de dezvoltare: modificare surs -> compilare -> testare (e xecu ie). Cerin e: scriptul va lansa editorul preferat pentru fiierul filename.c specific at ca parametru n linia de comand (sau citit de la tastatur, n caz contrar), apoi va interoga utilizatorul dac dorete s lanseze comp ilatorul i n caz afirmativ o va face (fiierul executabil s aib numele filename , deci fr sufixul .c). Apoi, dac sunt erori de c ompilare (lucru observabil prin erorile de compilare afiate de compilator), va relua ciclul de la editare (binen eles cu o pauz pentru ca utilizatorul s aib timp s citeasc erorile afiate

pe ecran), iar dac nu sunt erori la compilare, va interoga utilizatorul dac do rete s testeze (i.e., s execute) acel program i n caz afirmativ va executa acel fiier executabil rezultat prin compilare. n concluzie, la fiecare pas s se fac o interogare a utilizatorului dac dorete s co ntinue cu urmtorul pas. editcomp.sh : ============= #!/bin/bash if [ $# -eq 0 ] then echo -n "Dati numele fisierului sursa .c : " read fisier else fisier=$1 fi edt=1 while [ $edt -eq 1 ] do mcedit $fisier torului preferat

# se va inlocui comanda mcedit cu numele edi

done

echo -n "Doriti compilarea programului? (y/n) " read ans if [ $ans = "y" ] then out=`basename $fisier .c` gcc $fisier -o $out # compilare if [ $? -eq 0 ] # compilare fara erori then echo -n "Doriti executarea programului? (y/n) " read ans if [ $ans = "y" ] then ./$out edt=0 else echo "Finished ..." exit 0 fi else # erori la compilare echo "Apasa <Enter> pentru a continua ..." read edt # acest read are doar rol de pauza edt=1 fi else edt=0 fi

<!----------------------------------------------------------------------------------------------------------------------------------------------------->