1. Scopul lucrării Lucrarea de faţă prezintă caracteristicile interpretorului de comenzi, numit în Unix shell. Shell-ul reprezintă în acelaşi timp limbajul de comandă al sistemului de operare Unix şi un limbaj de programare. Ca limbaj de comandă shell-ul reprezintă interfaţa dintre utilizator şi sistemul de operare. Se permite definirea unor comenzi complexe cu ajutorul comenzilor elementare, ceea ce permite extinderea mediului de operare existent. Ca limbaj de programare sunt introduse noţiunile de variabilă, parametru, funcţie şi structuri de control specifice limbajelor algoritmice. 2. Consideraţii teoretice 2.1. Comenzi shell O comandă transmisă shell-ului pentru interpretare are formatul: command arguments unde command este numele programului care se execută şi arguments argumentele sale. Mai multe comenzi scrise pe o linie trebuie separate prin ;. Orice comandă executată întoarce un număr ce indică starea de ieşire din comanda. În Unix convenţia este 0 pentru o comandă executată cu succes şi diferit de 0 în caz de eşec. Comenzile pot fi conectate prin pipe ( simbolul |) astfel încât ieşirea unei comenzi constituie intrare pentru a doua. În acest caz starea de ieşire este a ultimei comenzi din pipe. De exemplu: ls -l | more aplică filtrul more pe rezultatul comenzii ls. Dacă secvenţa de comenzi este terminată cu ampersand (&), ele se execută asincron în background. Shell-ul afişează identificatorul procesului lansat. Continuarea unei comenzi pe linia următoare este posibilă dacă linia este terminată cu backslash (\). Caracterele && execută comanda de după ele dacă precedenta comandă a fost executată cu succes. În caz contrar se pot folosi caracterele ||. De exemplu: who | grep "adi" > /dev/null && echo "adi's logged on" O linie shell ce începe cu # este considerată comentariu. 2.2. Variabile shell Variabilele shell pot fi: variabile utilizator, parametri poziţionali şi variabile predefinite şi speciale. Variabile utilizator sunt definite prin: var1=val1 var2=val2 ... Conţinutul variabilei poate fi accesat prin prefixarea numelui variabilei cu caracterul $. De exemplu: loc=/usr/acct/adi cd $loc Variabilele utilizator sunt evaluate la valoarea lor, în afara cazului în care valoarea este delimitată de apostrofuri. Numele unei variabile nu este permis să fie egal cu numele unei funcţii. Dacă se doreşte definirea unei variabile noi prin concatenare unui nume de variabile cu un şir de caractere fix, numele variabilei trebuie delimitat de caracterele { şi }. De exemplu: $ num=3 $ k=${num}tmp $ echo $k 3tmp Shell-ul oferă un mecanism de substituţie bazat pe următoarele caractere: '...' Nici un caracter delimitat între apostrofuri nu are semnificaţie specială. "..." Nici un caracter delimitat de ghilimele nu are semnificaţie specială cu excepţia: $, ` si \. \c Nu interpretează caracterul c. Între ghilimele este caracter de evitare pentru: $, `, " si \. În alte cazuri este ignorat. `cmd` Execută comanda cmd. De exemplu: $ rep=`pwd` atribuie variabilei rezultatul comenzii pwd. La intrarea în sistem fiecare utilizator are o copie a programului shell. Acest shell păstrează un mediu distinct pentru fiecare utilizator din sistem. De exemplu: $ cat test x=50 echo :$x: $ x=100 $ test :50: $ echo $x 100 Acest exemplu demonstrează că mediul shell-ului iniţial conţine variabila x ce are valoarea 100 şi care nu este modificată la 50 de către programul shell test. Explicaţia este simplă, deoarece shell-ul iniţial lansează un subshell pentru execuţia programului test, fiecare shell având propria variabila x. Pentru a face cunoscută o variabilă unui subshell se foloseşte comanda export. De exemplu: $ y=10 $ cat test1 echo x = $x echo y = $y $ export y $ test1 x = y = 10 $ 1. Orice variabilă care nu este exportată este locală şi nu este cunoscută subshell-ului. 2. Variabilele exportate şi valorile lor sunt copiate în mediul subshell-ului, unde pot fi accesate şi modificate. Efectul modificări nu este vizibil în shell-ul părinte. 3. Dacă un subshell exportă explicit o variabilă a cărei valoare a modificat-o ea se transmite. Dacă subshell-ul nu exportă explicit o variabilă a cărei valoare a modificat-o modificarea afectează doar variabila locală, indiferent dacă variabila a fost exportată de shell-ul părinte. 4. O variabilă poate fi exportată înainte sau după atribuirea unei valori. Parametri poziţionali Parametrii poziţionali $1,... $9 sunt argumentele din linia de comandă, iar $0 este numele programului shell ce se execută. Parametrii poziţionali pot fi actualizaţi prin comanda set. Variabile predefinite şi speciale Există un număr de variabile predefinite folosite de interpretor sau de programele de deschidere a sesiunii. Acestea sunt: $CDPATH - Desemnează căile suplimentare de căutare la execuţia comenzii cd cu argument incomplet specificat. $HOME - Desemnează directorul implicit pentru comanda cd. $PATH - Defineşte lista directoarelor parcurse de shell în căutarea unui fişier executabil corespunzător comenzii introduse (directoarele sunt separate prin :). $IFS - Mulţimea caracterelor separatoare (blanc (040), \t (011), \n (012)), folosite de shell la analiza liniei de comandă. $MAIL - Indică numele unui fişier pe care shell-ul îl verifică pentru sosirea mailului. $PS1 - Defineşte prompterul shell, implicit $. $PS2 - Defineşte prompterul liniilor de continuare, implicit >. $SHELL - Indică numele shell-ului curent. Shell-ul atribuie valori implicite variabilelor: IFS, PATH, PS1, Ps2 la intrarea în sesiune. Programul login iniţializează cu valori implicite variabilele HOME şi MAIL. Variabilele speciale sunt read-only: $# - numărul argumentelor din linia de comandă (exclusiv $0). $* - argumentele $1, $2,..., $9 concatenate. $@ - argumentele $1, $2,...$9 separate. $? - starea de ieşire a ultimei comenzi executate. $$ - identificatorul de proces asociat shell-ului. $- - flagurile actuale ale shell-ului poziţionate prin set. $! - identificatorul ultimului proces lansat în background. 2.3. Programe (proceduri) shell Posibilitatea de a construi proceduri alcătuite din comenzi ale sistemului de operare constituie una din principalele facilităţi puse la dispoziţie de către shell. Interpretorul permite execuţia unor fişiere de comenzi tratate ca proceduri shell. Apelul unei proceduri este identic cu al unei comenzi: $procedura arg1 arg2 ... argn Procedura corespunde unui fişier de comenzi. Transmiterea parametrilor unei proceduri se face prin valoare. Apelul unei proceduri shell generează un proces shell fiu pentru citirea şi execuţia comenzilor. Execuţia unui fişier de comenzi se face prin: $ sh fis_com [ parametrii] sau $ fis_com [ parametrii] Forma a doua dacă fişierul desemnat, fis_com, are poziţionat bitul de execuţie din drepturile de acces ( comanda chmod u+x fis_com). Parametrilor actuali specificaţi în linia de apel le corespund parametrii formali $1, $2, ... $9 din interiorul procedurii. Numele fişierului de comenzi este referit în interior acesteia prin $0. Procedurile shell pot fi apelate recursiv. 2.4. Funcţii şi comenzi încorporate în shell Începând cu versiunea 2 din Unix System V în programe shell se pot defini funcţii. Formatul general pentru definirea unei funcţii este: func_name() { cmd1; ... cmd2; } unde func_name este numele funcţiei, parantezele marchează definirea funcţiei, iar între acolade este specificat corpul funcţiei. Se impune ca prima comandă să fie separată de acoladă cu un blanc, iar ultima comandă sa fie terminată cu caracterul ';' dacă acolada se află pe aceeaşi linie cu comandă. De regulă, dacă un utilizator şi-a definit mai multe funcţii într-un fişier, myfuncs, el poate face cunoscut shell-ului curent aceste funcţii prin: . myfuncs De exemplu, o funcţie utilă este cea de schimbare a directorului curent. Aceasta se bucură de proprietatea că ea se execută în mediul curent. Definiţia ei, conţinută în fişierul myfuncs, este: mycd() { crtdir=`pwd` if [ "$1" = "" ] then echo $olddir cd $oldir else cd "$1" fi oldir=$crtdir }Pentru a ilustra folosirea acestei funcţii se poate folosi secvenţa de comenzi: $ . myfuncs $ pwd /root/adi $ mycd /home/gabi $ pwd /home/gabi $ mycd - /root/adi Comparativ execuţia unei funcţii este mai rapidă decât a unui program shell echivalent, deoarece shell-ul nu necesită căutarea programului pe disc, deschiderea fişierului şi încărcarea conţinutului său în memorie. Ştergerea unei definiţii de funcţii este similară cu ştergerea unei variabile. Se foloseşte comanda unset func_name. Comenzile încorporate în shell pot fi apelate direct în programele shell. Lista şi efectul lor este prezentat în cele ce urmează. : Comandă fără efect. . file Se citesc şi se execută comenzile din fişierul file în mediul curent. Shell foloseşte variabila PATH pentru căutarea fişierului file. Fişierul nu trebuie să fie executabil. break [n] Comanda de părăsire a celei mai interioare bucle for, while sau until ce conţine break. Dacă n este specificat se iese din n bucle. De exemplu: while true do cmd=`getcmd` if [ "$cmd" = quit ] then break else processcmd "$cmd" fi done cd [dir] Schimbă directorul curent la cel specificat. Directorul curent este parte a mediului curent. Din acest motiv la execuţia unei comenzi cd dintr-un subshell doar directorul curent al subshell-ului este modificat. Există variabila $CDPATH ( Unix System V), care permite căutări suplimentare pentru comanda cd. De exemplu: $ echo $CDPATH :/usr/adi:/usr/adi/lab $ cd l1 /usr/adi/lab/l1 # având în vedere că l1 este subdirector pentru ultima cale. continue [n] Comanda permite trecerea la o nouă iteraţie a buclei for, while sau until. De exemplu: for file do if [ ! -f "$file" ] then echo "$file not found" continue fi # prelucrarea fişierului done echo [-n][arg] Comanda de afişare a argumentelor la ieşirea standard. Dacă opţiunea -n este specificată caracterul '\n' nu este scris la ieşirea standard. De exemplu: $ echo 'X\tY' X Y eval cmd Evaluează o comandă şi o execută. De exemplu: $ x='ab cd' $ y='$x' $ echo $y $x $ eval echo $y ab cd Se observă că eval parcurge lista de argumente de două ori: la transmiterea argumentelor spre eval şi la execuţia comenzii. Lucrul acesta este ilustrat de exemplul: $ pipe="|" $ ls $pipe wc -l | not found wc not found -l not found Rezultatul dorit se obţine prin: $ eval ls $pipe wc -l 5 Comanda eval este folosită în programe shell care construiesc linii de comandă din mai multe variabile. Comanda e utilă dacă variabilele conţin caractere care trebuie sa fie recunoscute de shell nu ca rezultat al unei substituţii. Astfel de caractere sunt: ;, |, &, < , >, ". exec prg Execută programul, prg, specificat. Programul lansat în execuţie înlocuieşte programul curent. Dacă exec are ca argument redirectarea I/E shell-ul va avea I/E redirectate. De exemplu: # contorizează numărul de linii dintr-un fişier
file=$1 count=0 exec < $file while read line do count=`expr $count + 1` done echo $count exit [(n)] Cauzează terminarea shell-ului curent cu cod de ieşire egal cu n. Dacă n este omis, codul de ieşire este cel al ultimei comenzi executate. export [v...] Marchează v ca nume de variabilă exportată pentru mediul comenzilor executate secvenţial ( pentru subshell). Dacă nu este precizat nici un argument se afişează o listă cu toate numele exportate în acest shell. Numele de funcţii nu poate fi exportat. getopts opt v Comanda este folosită la prelucrarea opţiunilor din linia de comandă. Se execută, de regulă, în bucle. La fiecare iteraţie getopts inspectează următorul argument din linia de comandă şi decide dacă este o opţiune validă sau nu. Decizia impune ca orice opţiune să înceapă cu caracterul '-' şi să fie urmată de o literă precizată în opt. Dacă opţiunea există şi este corect precizată, ea este memorată în variabila v şi comanda returnează zero. Dacă litera nu este printre opţiunile precizate în opt, comanda memorează în v un semn de întrebare şi returnează zero cu afişarea unui mesaj de eroare la standardul de eroare. În cazul în care argumentele din linia de comandă au fost epuizate sau următorul argument nu începe cu caracterul '-' comanda returnează o valoare diferita de zero. De exemplu, pentru ca getopts să recunoască opţiunile -a şi -b pentru o comandă oarecare, cmd, apelul este: getopts ab var Comanda cmd se poate apela: cmd -a -b sau cmd -ab În cazul în care opţiunea impune un argument suplimentar acesta trebuie separat de opţiune printr-un blanc. Pentru a indica comenzii getopts că urmează un argument după o opţiune, litera opţiunii trebuie postfixată cu caracterul ':'. De exemplu, dacă opţiunea b, din exemplul anterior, ar necesita un argument: getopts ab: var Dacă getopts nu găseşte după opţiunea b argumentul în variabila var se memorează un semn de întrebare şi se va afişa un mesaj de eroare la ieşirea standard. În caz că argumentul există el este memorat într-o variabilă specială OPTARG. O altă variabilă specială, OPTIND, este folosită de comandă pentru a preciza numărul de argumente prelucrate. Valoarea ei iniţială este 1. hash [-r][cmd] Permite căutarea unor comenzi şi memorarea locului unde se găsesc acestea în structura arborescenta de fişiere. Dacă argumentul lipseşte sunt afişate toate comenzile memorate. Dacă opţiunea -n este specificată se şterg comenzile memorate. pwd Cauzează afişarea directorului curent. read [v...] Se citeşte o linie din fişierul standard de intrare şi se atribuie cuvintele citite variabilelor v ... (dacă IFS nu a fost redefinită). De exemplu: $ IFS=: $ read x y z 123:345:678 $ echo $x 123 readonly [v...] Identică cu read, dar valoarea variabilei v nu poate fi schimbată prin atribuiri ulterioare. Dacă argumentul lipseşte se afişează variabilele read-only. return [n] Permite revenire dintr-o funcţie cu valoarea n. Dacă n este omis, codul returnat este cel al ultimei comenzi executate. Valoarea returnată poate fi accesată prin variabila $? şi poate fi testată în structurile de control if, while şi until. De exemplu:
testare() { if [ "$1" = "" ] fi # test . testare # funcţia se face cunoscută testare $1 if [ "$?" = 0 ] then set [--aefhkntuvx] [args] Comanda permite activarea/inactivarea unor opţiuni sau pentru poziţionarea parametrilor poziţionali. Starea curentă se află în $-. Argumentele rămase sunt atribuite, rând pe rând, la $1, $2,.... Dacă nu există argumente, valoarea fiecărei variabile este afişată. Opţiuni: -v Afişează toate liniile programului shell. -x Afişează toate comenzile şi argumentele lor pe măsură ce sunt executate (prefixate de +). Folosită la depanarea programelor shell. -e Shell-ul se opreşte dacă vreo comandă returnează cod de ieşire eronat; -n Permite testarea programelor shell, fără a le executa. De exemplu: $ set unu doi trei $ set -vx $ echo $1:$2:$3 $ set +x unu:doi:trei $ echo $# 3 $ echo $* unu doi trei shift [n] Deplasare spre dreapta (cu n) a parametrilor. sleep n Suspendă execuţia pentru n secunde. test condiţie Comanda este echivalentă cu [ condiţie ]. Shell-ul evaluează condiţia iar dacă rezultatul evaluării este TRUE se returnează codul de ieşire 0. Condiţia poate fi formată din mai multe condiţii legate prin operatorii -a (and) şi -o (or). Condiţiile pot fi delimitate prin paranteze dar gardate de \. times Shell-ul afişează timpul ( timp utilizator şi timp sistem) consumat de procesele executate de shell. De exemplu: $ times 2m7s 3m7s trap cmds sem Shell-ul execută comanda sau comenzile cmds la recepţionarea semnalului sem. De exemplu, pentru ca la apăsarea tastei Ctr-C ( semnalul 2=interrupt) să se şteargă anumite fişiere se foloseşte: $ trap 'm $TMPFILE; exit' 2 Pentru a ignora un semnal se poate folosi: $ trap "" 2 Pentru a reseta acţiunea ataşată unui semnal: $ trap 2 type cmds Furnizează informaţii despre comanda sau comenzile cmds. Informaţia specifică dacă comanda este: o funcţie, un program shell, o comandă Unix standard. De exemplu: $ type mycd mycd is a function # urmează definiţia ... $ type pwd pwd is a shell builtin $ type troff troff is /usr/bin/troff unset v Pentru a şterge definirea unei variabile din mediul curent se foloseşte comanda unset. De exemplu: $ x=100 $ echo $x 100 $ unset x $ echo $x # variabila x a fost ştearsă $ wait [pid] Aşteaptă execuţia procesului pid. Dacă pid este omis shell-ul aşteaptă terminarea tuturor proceselor fiu. De exemplu: $ sort fisdate > fisdate_sort & 123 $... $ wait 123 $ # prompterul apare la terminarea procesului 123 Pentru a aştepta un anume proces se poate folosi variabila $!. De exemplu: prog1& pid1=$! ... prog2& pid2=$! ... wait $pid1 # se aşteaptă terminarea procesului pid1 ... wait $pid2 # se aşteaptă terminarea procesului pid2 2.5. Structuri de control După execuţia unei comenzi se returnează un cod de revenire, care poate fi testat în structurile de control condiţionale. O comandă corect executată întoarce cod de retur zero. O comandă este o secvenţa de cuvinte separate prin caractere '\b' sau '\t'. Primul cuvânt specifică comanda ce se execută, iar următoarele specifică argumentele comenzii. Valoarea unei comenzi este starea ei de ieşire dacă execuţia ei a decurs normal sau 200+stare (octal), dacă execuţia ei s-a terminat anormal. Un lanţ (pipe) de comenzi este o secvenţă de una sau mai multe comenzi separate prin caracterul '|'. Ieşirea unei comenzi serveşte ca intrare pentru următoarea. Fiecare comandă este un proces separat, shell-ul aşteptând execuţia ultimei comenzi din lanţ. Starea unui lanţ este starea ultimei comenzi din lanţ. O listă este o secvenţă de una sau mai multe lanţuri separate prin caracterele ';', '&' sau && şi ||. Separatorii afectează execuţia astfel: ; Execuţie secvenţială. & Execuţie asincronă (background). && Execută următoarea listă dacă precedentul lanţ a returnat starea 0. || Identic cu &&, dar starea de ieşire diferită de 0. O comandă este o comandă simplă sau una dintre comenzile de mai jos. a) Structura IF are sintaxa: if lista_1 then lista [ elif lista_2 then lista] [ else lista] fi Se execută lista_1; dacă codul de retur este zero se execută lista ce urmează primului then. În caz contrar, se execută lista_2 şi dacă codul de retur este zero se execută lista de după al doilea then. De exemplu: if test -f $1 then echo $1 este un fişier ordinar elif test -d $1 then echo $1 este un director else echo $1 este necunoscut fi b) Structura CASE are sintaxa: case cuvânt în şablon_1) lista;; şablon_2) lista;; ... esac Se compară cuvânt cu fiecare din şabloanele prezente şi se execută lista de comenzi unde se constată potrivirea. De exemplu, analiza unei opţiuni din linia de comandă se poate face astfel: case $1 în -r) echo opţiunea r;; -m) echo opţiunea m;; *) ;; esac c) Structura FOR are sintaxa: for nume [in cuvânt ...] do lista done Variabila de buclă nume ia pe rând valorile din listă ce urmează lui in. Pentru fiecare valoare se execută ciclul for. Dacă în cuvânt este omis, ciclul se execută pentru fiecare parametru poziţional actualizat. Condiţia poate fi şi în * , caz în care variabila nume ia pe rând ca valoare numele intrărilor din directorul curent. De exemplu, pentru a copia trei fişiere cu nume similare în directorul /TMP se foloseşte secvenţa: for $1 în 1 2 3 do cp fis{$1} /TMP done d) Structura WHILE are sintaxa: while lista_1 do lista done Dacă starea de ieşire din ultima comandă din lista_1 este zero, se execută lista. În caz contrar, bucla se termină. De exemplu, pentru a vedea dacă o persoană este în sesiune se poate folosi secvenţa : while ; do if who | grep $1 /dev/null then echo $1 este prezent exit else sleep 120 done Argumentul buclei while este comandă vidă, care returnează codul 0. Bucla se va executa până utilizatorul, al cărui nume este dat de primul argument al procedurii, este în sesiune. e) Structura UNTIL are sintaxa: until lista_1 do lista done Ea este similară structurii WHILE, dar se neagă condiţia de terminare a testului. De exemplu: until who | grep $1 dev/null do sleep 120 done echo $1 este prezent 2.6. Redirectarea fişierelor standard Procesul shell deschide trei fişiere standard ( cu descriptori 0, 1, 2) pentru intrarea, ieşirea şi ieşirea de erori, ce sunt atribuite terminalului la care s-a deschis sesiunea respectivă. Aceste fişiere sunt asociate şi utilizate într-o maniera standard de fiecare program. La execuţia unei comenzi procesul asociat creat de shell moşteneşte fişierele deschise, inclusiv cele standard. Acest lucru asigură independenţa programelor faţă de dispozitivele fizice asociate de interpretor fişierelor standard. Interpretorul permite însă redirectarea fişierelor standard de intrare/iesire spre alte dispozitive periferice sau fişiere astfel: < fis fişşierul fis este fişierul standard de intrare; > fis fişierul fis este fişierul standard de ieşire; >>fis fişierul fis este fişierul standard de ieşire în adăugare; <<fis fişierul standard de intrare este intrarea shell ( citeşte până la linia identica cu fis, sau până la sfârşit de fişier); <& nr utilizează fişierul cu descriptorul nr ca intrare standard; >& nr similar dar pentru ieşirea standard; <&- închide intrarea standard; >&- închide ieşirea standard. Exemple: a) $cat fis > /dev/lp - listează fişierul la imprimantă; b) $cat f1 f2 > f3 - concatenează fişierele f1 şi f2 în f3. Dacă fişierul f3 există deja, prin redirectare cu > vechiul conţinut este pierdut. c) $cat f1 f2 >> f3 - dacă f3 există deja, la vechiul sau conţinut se adăugă rezultatul concatenării fişierului f1 şi f2. d) $mail user1 < scris - trimite utilizatorului user1 o scrisoare aflată în fişierul scris. e) $echo "Invalid" >& 2 - scrie mesajul la ieşirea standard. d) $wc -l <<END - execută comanda wc până la întâlnirea linie cu cuvântul 'END'. >o linie >încă una >END 2 3. Aplicaţii 3.1. Fişierul de comenzi de mai jos creează un fişier de articole, ce constituie nume de persoane: echo Creare fişier: echo Nume fişier[fnnn]: \\c" read fname valid $fname if test -f invalid then echo Nume invalid m invalid exit fi echo > $fname aux=0 echo Introduceţi articolele: while read string case $string în [a-zA-Z]*);; *) aux=1;; esac test $aux -eq 0 do echo $string >> $fname done sort $fname -o $fname echo Fişierul creat: echo cat $fnameProcedura valid: case $1 în f[3-5][1-6][1-6][1-6]) ;; *) echo > invalid;; esac3.2. Următorul fişier de comenzi afişează toate fişierele dintr-un subdirector, citit ca argument din linia de comanda. Fişierul de comenzi se apelează astfel: sh prtr work > /dev/lp0 $cat prtr echo "cpy Fişiere de comenzi (C) Laborator SO Unix Adi K.1994" echo echo $1: echo if test -d $1 then ls -a $1/* for nume în $1/.[a-z,A-Z]* $1/* do if test -r $nume then sh prtr $nume fi done else cat $1 fi3.3. Fişierul de comenzi de mai jos permite copierea unui fişier în alt fişier sau mai multor fişiere într-un director. # preia argumentele nrargs=$# filelist= copylist= echo "cpy Fişiere de comenzi (C) Laborator SO Unix Adi K. 1995" # salvează argumentele mai puţin ultimul în filelist while [ "$#" gt 1 ] do filelist="$filelist $1" shift done dest="$1" # dacă sunt: a) mai puţin de două argumente, # sau b) mai mult de doua fără ca ultimul să fie director if [ "$nrargs" lt 2 o "$nrargs" gt 2 a ! d "$dest" ] then echo "Usage: cpy file1 file2" echo " cpy file(s) dir" exit 1; fi # construieşte destinaţia for from în $filelist do if [ d "$dest" ] then destfile="$dest/`basename $from`" else destfile="$dest" fi # pentru fiecare fişier din filelist se verifică dacă acesta # există şi se afişează un mesaj de suprascriere. Dacă răspunsul e # yes sau dacă fişierul nu există acesta este adăugat la copylist. if [ f "$destfile" ] then echo n "$destfile already exists; Overwrite (yes/no)?:" read answer if [ "$answer" = yes ] # se lasă spaţiu pentru operator! then copylist="$copylist $from" fi else copylist="$copylist $from" fi done if [ n "$copylist" ] then echo e "Copy \tFrom: $copylist" echo e "\tTo: $dest" cp $copylist $dest fi 4. Probleme propuse pentru rezolvare 4.1. Ce afişează secvenţele de comenzi: a) eval echo \$$# b) x=100 px=x eval echo \$$px eval $px=5 echo $x c) trap 'echo logged off at `date` >> $HOME/logoffs' 0 trap logout d) ls -l > fis 2>&1 e) exec 2>/tmp/err f) wc <&- g) line="Laborator SO: Unix: Anul IV" IFS=: set $line echo $# for câmp; do echo $câmp; done h) file=$1 count=0 while read line do count=`expr $count + 1` done < $file echo $count Notă: bucla while este executată într-un subshell deoarece intrarea ei este redirectată din $file. 4.2. Să se precizeze ce realizează comenzile: who | wc -l > fis ls *.c | wc -l >> fis who | sort myexe 2>error & sort < fis | wc 4.3. Folosind comanda eval să se scrie un program, recho, care afişează argumentele sale în ordine inversă. Se presupune că programul nu admite mai mult de nouă argumente în linia de comandă. 4.4. Fişierul de comenzi de mai jos decide dacă două directoare sunt identice din punct de vedere al conţinutului lor în fişiere sursă C. Programul are însă o eroare. Care este ea ? crtdir=`pwd` if [ d $1 ] then if [ d $2 ] then cd $1 ls R > $crtdir/f1 cd $crtdir cd $2 ls R > $crtdir/f2 cd $crtdir # rămâne numai ce e cu ".c" ordonat alfabetic grep '.c$' f1 > f11 grep '.c$' f2 > f22 m f1 f2 if cmp f11 f22 then echo "Directories are equal" else echo "Different directories" fi m f11 f22 else echo "$2 isn't directory" fi else echo "$1 isn't directory" fi4.5. Să se scrie un fişier de comenzi, care verifică dacă două directoare sunt egale. Numele celor două directoare se transmit ca argumente în linia de comandă. Două directoare se consideră că sunt egale dacă conţin aceleaşi fişiere. Nu contează ordinea lor. 4.6. Să se scrie un fişier de comenzi care permite căutarea unui fişier în întreaga structura a unui subdirector. Argumentele se precizează în linia de comandă. 4.7. Să se scrie un fişier de comenzi care şterge toate sursele C dintr-un director dacă ele se găsesc în structura altui director. Primul argument din linia de comandă este directorul în care se afla sursele C, iar al doilea este directorul de unde începe căutarea. 4.8. Să se scrie un fişier de comenzi care copiază o întreaga structură de subdirectore ca structură a unui alt subdirector. Cele două subdirectoare se citesc ca argumente din linia de comandă. 4.9. Să se scrie două fişiere de comenzi care să se sincronizeze între ele. 4.10. Să se verifice şi să se explice ce realizează secvenţa de comenzi: ftp n server2 >tmp <<gata user adi adi cd edi mget FIS y gata Notă: 'server2' este numele unui server Unix. |