Lucrarea 3

Fişiere de comenzi


 
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()
{
echo "$1"
if [ "$1" = "" ]
then return 0
else return 1
fi
}
# test
. testare # funcţia se face cunoscută
testare $1
if [ "$?" = 0 ]
then
echo "Argumentul e corect"
else
echo "Argumentul e incorect"
fi
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 $fname 
Procedura valid:
case $1 în 
f[3-5][1-6][1-6][1-6]) ;; 
*) echo > invalid;; 
esac 
3.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 
fi 
3.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" 
fi 
4.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.