Sunteți pe pagina 1din 32

Programare în Shell

Utilizarea Sistemelor de Operare

Paul Irofti
Universitatea din Bucures, ti
Facultatea de Matematică s, i Informatică
Department de Informatică
Email: paul.irofti@fmi.unibuc.ro
Motivat, ie

I de multe ori vrem să avem o singură comandă pentru un s, ir de


comenzi
I comenzile sunt preponderent comenzi shell sau din sistem, nu
operat, ii logice sau aritmetice
I avem nevoie să scriem un program scurt rapid
I vrem să funct, ioneze pe orice alt sistem pentru a repeta un set
de operat, ii de configurare sau administrare
I bazat pe CS2043 Unix and Scripting de la Cornell University
(cursurile 9 s, i 10)
Variante Shell

I sh(1) – Bourne shell


I printre primele shelluri
I disponibilă oriunde în /bin/sh
I funct, ionalitate redusă
I portabilitate
I csh(1) – C shell, comenzi apropiate de lucru în C
I ksh(1) – Korn shell, bazat pe sh(1), succesor csh(1)
I bash(1) – Bourne Again shell
I cea mai răspândită
I există s, i în Windows 10
I majoritatea scripturilor moderne scrise în bash(1)
I acest rezultat a dus la o lipsă de portabilitate
I proiect mare cu multe linii de cod s, i probleme de securitate
Variabile Shell

I două tipuri de variabile: locale s, i de mediu


I variabilele locale există doar în instant, a curentă a shell-ului
I convent, ie: variabilele locale se notează cu litere mici
I asignare: x=1 (fără spat, ii!)
I x = 1: se interpretează drept comanda x cu argumentele = s, i 1
I folosirea valorii unei variabile se foloses, te prin prefixarea
numelui cu $
I cont, inutul oricărei variabile poate fi afis, at cu comanda
echo(1)
$ x=1
$ echo $x
1
Variabile de mediu
I folosite de sistem pentru a defini modul de funct, ionare a
programelor
I shell-ul trimite variabilele de mediu proceselor sale copil
I env(1) – afis, ează toate variabilele de mediu setate
I variabile importante
I $HOME – directorul în care se t, in datele utilizatorului curent
I $PATH – listă cu directoare în care se caută executabilele
I $SHELL – programul de shell folosit implicit
I $EDITOR – programul de editare fis, iere implicit
I $LANG – limba implicită (ex. ro_RO.UTF-8)
I export(1) – se foloses, te pentru a seta o variabilă de mediu
I printenv(1) – se foloses, te pentru a afis, a o variabilă de mediu
$ e x p o r t V=2
$ printenv V
2
$ e c h o $V
2
Exemplu: Variabilă locală

$ LANG=ro_RO . UTF−8
$ p r i n t e n v LANG
$ echo $LANG
ro_RO . UTF−8
$ bash
bash −4.4 $ p r i n t e n v LANG
bash −4.4 $ t
b a s h : t : command n o t f o u n d
bash −4.4 $ echo $LANG

bash −4.4 $ e x i t
Exemplu: Variabilă de mediu

$ e x p o r t LANG=ro_RO . UTF−8
$ p r i n t e n v LANG
ro_RO . UTF−8
$ bash
bash −4.4 $ p r i n t e n v LANG
ro_RO . UTF−8
bash −4.4 $ echo $LANG
ro_RO . UTF−8
bash −4.4 $ t
b a s h : t : comand ă neg ă s i t ă
Variable expansion
Variabilele pot fi mai mult decât simplii scalari
I $(cmd) – evaluează întâi comanda cmd, iar rezultatul devine
valoarea variabilei
$ e c h o $ ( pwd )
/home/ p a u l
$ x=$ ( f i n d . −name \ ∗ . c )
$ e c h o $x
. / b a t l e f t . c . / p c i e . c . / maxint . c . / e i s a i d . c

I $((expr)) – evaluează întâi expresia aritmetică, iar rezultatul


devine valoarea variabilei
$ x=1
$ e c h o $ ( ( x++))
$ echo $ ((1+1))
1
2
$ e c h o $ ( ( x++))
$ e c h o $ ( ( x +1))
2
2
$ e c h o $((++x ) )
$ e c h o $ ( ( x <1))
4
0
Quoting
S, irurile de caractere sunt interpretate diferit în funct, ie de citare:
I Single quotes ”
I toate caracterele îs, i păstrează valoarea
I caracterul ’ nu poate apărea în s, ir, nici precedat de "\"
I exemplu:
$ echo ’Am v a r i a b i l a $x ’
Am v a r i a b i l a $x
I Double quotes ""
I caractere speciale $ ‘ \ (opt, ional !)
I restul caracterelor îs, i păstrează valoarea
I exemplu:
$ echo "$USER h a s home i n $HOME"
p a u l h a s home i n /home/ p a u l
I Back quotes “ – funct, ionează ca $()
$ echo " Today i s ‘ da t e ‘ "
Today i s Wed May 2 1 8 : 0 0 : 0 8 EEST 2018
Înlănt, uirea comenzilor

I cmd1; cmd2 – înlănt, uire secvent, ială, cmd2 imediat după cmd1
I cmd1 | filtru | cmd2 – ies, irea comenzii din stânga este
intrarea celei din dreapta operatorului |
I cmd1 && cmd2 – execută a doua comandă doar dacă prima s-a
executat cu succes
I cmd1 || cmd2 – execută a doua comandă doar dacă prima a
es, uat
I exemplu:
$ m k d i r a c t e && mv ∗ . docx a c t e /
$ l s −l R | t e e f i l e s . l s t | wc − l
$ s s h e x a m p l e . o r g | | echo " C o n n e c t i o n f a i l e d ! "
Scripting

Definition
Script-urile sunt programe scrise pentru un mediu run-time specific
care automatizează execut, ia comenzilor ce ar putea fi executate
alternativ manual de către un operator uman.

I nu necesită compilare
I execut, ia se face direct din codul sursă
I de acea programele ce execută scriptul se numesc
interpretatoare înloc de compilatoare
I comenzile executate se mai numesc s, i byte-code
I astăzi granit, a dintre compilatoare s, i interpretatoare nu mai
este atât de clară (JIT, tiered compilation)
I exemple: perl, ruby, python, sed, awk, ksh, csh, bash
Shebang (#!)

I reprezintă prima linie din orice script Unix


I are forma #!/path/to/interpreter
I exemple: #!/bin/sh, #!/usr/bin/python
I pentru protabilitate folosit, i env(1) pentru a găsi unde este
instalat interpretatorul
I exemple: #!/usr/bin/env ruby, #!/usr/bin/env perl
I oriunde altundeva în script # este interpretat ca început de
comentariu s, i restul linei este ignorată (echivalent // în C)
I introdusă de Denis Ritchie circa 1979
Exemplu: hello.sh

1. Scriem fis, ierul hello.sh cu comenzile dorite


#!/ b i n / sh

# Salute the user


echo " H e l l o , $USER ! "

2. Permitem execut, ia: chmod +x hello.sh


3. Executăm:
$ . / h e l l o . sh
Hello , paul !
Variabile speciale în scripturi

I $1, $2, ..., ${10}, ${11}, ... – argumentele în ordinea primită


I dacă numărul argumentului are mai mult de două cifre, trebuie
pus între acolade
I $0 – numele scriptului (ex. hello.sh)
I $# – numărul de argumente primite
I $* – toate argumentele scrise ca "$1 $2 ... $n"
I $@ – toate argumentele scrise ca "$1" "$2" ... "$n"
I $? – valoarea ies, irii ultimei comenzi executate
I $$ – ID-ul procesului curent
I $! – ID-ul ultimului proces suspendat din execut, ie
Exemple: argumente script
I add.sh – adună două numere
#!/ b i n / sh
echo $ ( ( $1 + $2 ) )
apel:
$ sh add . sh 1 2
3
I tolower.sh – imită funct, ia din C tolower(3)
#!/ b i n / sh
t r ’ [ A−Z ] ’ ’ [ a−z ] ’ < $1 > $2
apel:
$ echo "WHERE ARE YOU?" > s c r e a m i n g . t x t
$ . / t o l o w e r . sh screaming . t x t decent . t x t
$ cat decent . txt
where a r e you ?
Blocuri de control

Pentru scripturi mai complexe avem nevoie, ca în orice limbaj, de


blocuri de control
I condit, ionale – if, test [ ], case
I iterative – for, while, until
I comparative – -ne, -lt, -gt
I funct, ii – function
I ies, iri – break, continue, return, exit
If

I cuvinte cheie: if, then, elif, else, fi


I exemplu ce foloses, te toate cuvintele cheie:
if cmd1
then
cmd2
cmd3
e l i f cmd4
then
cmd5
cmd6
else
cmd7
fi
I o condit, ie este adevărată dacă comanda asociată este
executată cu succes ($?=0)
Exemplu: if

Caută în fis, ier date s, i le adaugă dacă nu le găses, te


#!/ b i n / s h
i f g r e p " $1 " $2 > / dev / n u l l
then
e c h o " $1 f o u n d i n f i l e $2 "
else
e c h o " $1 n o t f o u n d i n f i l e $2 , a p p e n d i n g "
e c h o " $1 " >> $2
fi

apel:
$ . / t e x t . s h who d e c e n t . t x t
who n o t f o u n d i n f i l e d e c e n t . t x t , a p p e n d i n g
$ cat decent . txt
where a r e you ?
who
test sau [ ]

I nu dorim să testăm tot timpul execut, ia unei comenzi


I există expresii pentru a compara sau verifica variabile
I test expr – evaluează valoarea de adevăr a lui expr
I [ expr ] – efectuează aceias, i operat, ie (atent, ie la spat, ii!)
I expresiile pot fi legate logic prin
I [ expr1 -a expr2 ] – s, i
I [ expr1 -o expr2 ] – sau
I [ ! expr ] – negat, ie
Expresii test: numere

I [ n1 -eq n2 ] – n1 = n2
I [ n1 -ne n2 ] – n1 6= n2
I [ n1 -ge n2 ] – n1 ≥ n2
I [ n1 -gt n2 ] – n1 > n2
I [ n1 -le n2 ] – n1 ≤ n2
I [ n1 -lt n2 ] – n1 < n2
Expresii test: s, iruri de caractere

I [ str ] – str are lungime diferită de 0


I [ -n str ] – str nu e gol
I [ -z str ] – str e gol ("")
I [ str1 = str2 ] – stringuri identice
I [ str1 == str2 ] – stringuri identice
I [ str1 != str2 ] – stringuri diferite
Expresii test: fis, iere

I -e path – verifică dacă există calea path


I -f path – verifică dacă path este un fis, ier
I -d path – verifică dacă path este un director
I -r path – verifică dacă avet, i permisiunea de a citi path
I -w path – verifică dacă avet, i permisiunea de a scrie path
I -x path – verifică dacă avet, i permisiunea de a executa path
while

I execută blocul cât timp comanda cmd se execută cu succes


w h i l e cmd
do
cmd1
cmd2
done
I în loc de comandă putem avea o expresie de test
I într-o singură linie: while cmd; do cmd1 cmd2; done
Exemplu: while

Afis, ează toate numerele de la 1 la 10:


i =1
w h i l e [ $ i − l e 10 ]
do
echo " $ i "
i=$ ( ( $ i +1))
done
Sau într-o singură linie:
i=1; while [ $i -le 10 ]; do echo "$i"; i=$(($i+1));
done
until

I execută blocul cât timp comanda cmd se execută fără succes


u n t i l cmd
do
cmd1
cmd2
done
I în loc de comandă putem avea o expresie de test
I într-o singură linie: until cmd; do cmd1 cmd2; done
for

I execută blocul pentru fiecare valoare din listă


f o r var in s t r 1 s t r 2 . . . strN
do
cmd1
cmd2
done
I var ia pe rând valoarea str1, pe urmă str2 până la strN
I de regulă comenzile din bloc (cmd1, cmd2) sunt legate de var
I comanda for are mai multe forme, aceasta este cea mai
întâlnită
I într-o singură linie:
for var in str1 str2 ... strN; do cmd1 cmd2; done
Exemplu: for

Compileză toate fis, ierele C din directorul curent


for f in ∗. c
do
echo " $ f "
c c $ f −o $ f . o u t
done
Sau într-o singură linie:
for f in *.c; do echo "$f"; cc $f -o $f.out; done
for tradit, ional

I formă întâlnite doar în unele shell-uri, nu este portabil


I execută blocul urmând o sintaxă similară C
f o r ( ( i =1; i <=10; i++ ) )
do
echo $ i
done
I i ia pe rând toate valorile între 1 s, i 10
I în exemplu, blocul afis, ează $i, dar pot apărea oricâte alte
comenzi
I într-o singură linie:
for (( i=1; i<=10; i++ )); do cmd1 cmd2; done
Case

I se comportă similar cu switch în C


case var in
pattern1 )
cmd1
cmd2
;;
pattern2 )
cmd3
;;
∗ )
defaultcmd
;;
esac
I pattern – poate fi un string sau orice expresie de shell (similar
cu wildcards-urile folosite pentru grep(1))
Exemplu: case
Parsare subcomandă pentru un program SQL:
c a s e " $1 " i n
’CREATE ’ )
do_creat
;;
’ INSERT ’ )
do_insert
;;
’ SELECT ’ )
do_select
;;
’ USE ’ )
do_use
;;
’DROP’ )
do_drop
;;
∗)
print_usage
esac
read

I cites, te una sau mai multe variabile din stdin


I sintaxă: read var1 var2 ... varN
$ read x y
1 2
$ echo $x $y
1 2
I dacă nu este dată nici o variabilă, se as, teaptă să fie una
singură s, i pune rezultatul în $REPLY
$ read
hello
$ echo $REPLY
hello
I citire line cu linie dintr-un fis, ier:
cat foo.txt | while read LINE; do echo $LINE done
Depanare

Pentru a depana un script apelat, i-l cu shell-ul folosit s, i opt, iunea -x.
Comenzile executate apar pe ecran prefixate cu + .
$ s h −x t o l o w e r . s h s c r e a m i n g . t x t d e c e n t . t x t
+ t r [ A−Z ] [ a−z ]
+ < screaming . txt
+ > decent . txt

S-ar putea să vă placă și