Shell scripting
Shell-ul este principala interfață de comunicare între utilizator și sistemul de operare. Deși, în mod intuitiv, shell-ul este identificat cu o interfață în linia de comandă, poate fi și o interfaţă grafică. Exemplu este File Explorer-ul sistemului de operare Microsoft Windows.
În cele ce urmează vom studia interfaţa de tip CLI (Command Line Interface) oferită de sistemele de operare Unix. Deși cu o curbă de învăţare mai mare decât o interfaţă grafică, CLI permite un control mult mai bun al sistemului. Mai mult, shell-ul dispune de un limbaj de programare. Un program shell, denumit script shell, este folosit pentru a îmbina mai multe comenzi și diverse structuri de control pentru a obţine o nouă funcţionalitate sau pentru automatizarea sarcinilor. În acest fel un script shell este un instrument esenţial pentru sarcinile administrative și alte rutine repetitive care nu necesită funcţionalităţi ale unor limbaje de programare avansate.
În continuare ne vom referi la Bash (Bourne Again SHell). Există și alte shell-uri pe sisteme Unix precum tcsh, zsh, ash, etc. De curând, Microsoft oferă PowerShell pe sistemele Windows. PowerShell are o abordarea orientată pe obiecte și un set de funcţionalităţi care acoperă nevoile de administrare ale unui sistem Windows.
Cel mai simplu script shell
Un script simplu care doar afișează mesajul "Hello, World!" este următorul:
Execuţia acestuia este următoarea:
Se observă că este necesar ca fișierul să fie executabil pentru a putea fi interpretat. Șirul #! de la începutul fișierului poartă denumirea de shebang. Acesta indică sistemului ce program va fi invocat pentru a interpreta scriptul. Exemple pot fi:
Spre exemplu, următorul script se șterge pe sine:
Un script poate fi rulat prin precizarea explicită a interpretorului în linia de comandă:
În această situaţie nu este nevoie ca scriptul sa fie executabil și nici nu este nevoie de prezenţa liniei #!/bin/bash.
Caracterul # semnifică începutul unui comentariu care durează pană la sfarșitul liniei.
Comanda exit este folosită pentru a indica valoarea de retur a scriptului. Este implicit 0 (cu alte cuvinte nu era necesar să apară în script).
Operatori shell
Shell-ul prezintă o serie de operatori folositi pentru îmbinarea comenzilor.
Concatenarea comenzilor
Următorii operatori sunt folosiți pentru concatenarea diverselor comenzi:
command1 ; command2 - comenzile sunt executate secvenţial (una dupa alta)
command1 && command2 - command2 este executată numai dacă command1 are valoare de retur 0
command1 || comand2 - command2 este executată numai dacă command1 are valoare de retur diferita de 0
Înlănțuirea comenzilor
Inlănțuirea comenzilor se realizează folosind operatorul | (pipe). În această situatie ieșirea unei comenzi devine intrarea pentru cealaltă comandă.
Câteva exemple sunt prezentate în continuare:
Redirectări
Comenzilor le pot fi redirectate, respectiv, intrarea standard, ieșirea standard și eroarea standard dintr-un fișier. O parte din operatorii folosiți pentru redirectare sunt:
> - redirectarea ieșirii standard
2> - redirectarea erorii standard
2>&1 - redirectarea erorii standard in ieșirea standard. Efectiv, unificarea stderr cu stdout.
< - redirectarea intrării standard
Exemple:
Variabile
Ca orice limbaj, shell-ul permite utilizarea de variabile. Spre deosebire de limbajele cunoscute, variabilele shell nu au tipuri. O variabila poate fi evaluată atât ca număr cât și ca șir de caractere.
Exemple de definire de variabile:
ATENTIE! Sintaxa shell este foarte strictă; NU este permis sa existe spatii intre numele variabilei si caracterul = sau intre caracterul = si valoarea variabilei.
Se observă că valoarea unei variabile este referită prin folosirea simbolului $.
Exemple de folosire de variabile:
Argumente în linia de comandă
Un script poate primi argumente în linia de comandă. Argumentele sunt referite respectiv folosind parametrii poziționali: $1, $2, ... $0 este numele scriptului (echivalent cu argv[0] din C).
Numărul de argumente din linia de comandă este dat de 'ParseError: KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲'. '#' va fi 0 daca nu avem argumente in linia de comandă (echivalentul C - argc - ar fi avut valoarea 1 in acest caz).
$@ poate fi folosit pentru obținerea întregii liste de argumente separate prin spațiu.
Exemplu:
Rularea scriptului este:
Caractere speciale
Un set de caractere au semnificație specială in cadrului shell-ului.
spațiu
Caracterul spațiu (blank) este separator pentru argumentele in linia de comandă sau pentru elementele unei liste. Dacă se dorește transmiterea ca parametru a unui argument ce conține spatiu acesta trebuie citat (quoted):
backslash
Caracterul backslash forțeaza caracterul ce-l precede să-și păstreze semnificația literală; cu alte cuvinte, este folosit pentru a întârzia (a escapa) acel caracter:
ghilimele
Un șir între ghilimele (double quotes ") va păstra semnificația literală a caracterelor ce-l compun cu excepția caracterelor ' (apostrof) și , ', ``, \ sau newline.
apostrof
Un șir între caractere apostrof (single quotes) va păstra semnificatia literală a tuturor caracterelor ce-l compun (nu există excepții).
dollar - expansion
Caracterul dollar este folosit în mai multe situații în ceea ce se numește expansion. Este folosit pentru a recupera valoarea unei variabile, pentru a stoca într-o variabila ieșirea unei funcții, etc.
expandarea unui parametru
Se înlocuiește un parametru (variabila) sau se înlocuiesc parametrii poziționali. $? se traduce in valoarea de retur a ultimei comenzi executate.
substitutia unei comenzi
Ieșirea unei comenzi inlocuiește comanda efectivă:
expansiune aritmetică
Se realizează evaluarea unei expresii aritmetice cu furnizarea rezultatului:
Structuri de control
Ca orice limbaj de programare, shell-ul are un set de structuri de control pentru a permite lucrul cu cicluri și cu decizii.
if
Sintaxa pentru if este următoarea:
Structurile elif și else sunt opționale.
Condiția poate aparea în două formate:
[ conditie_efectiva ]
sau
test conditie_efectiva
Pentru condițiile posibile consultați pagina de manual a utilitarului test.
ATENTIE! Dacă folosiți prima variantă este nevoie de spatiu după [ si inainte de ].
Exemple:
case
Sintaxa pentru case este următoarea:
Se poate folosi ca pattern * pe post de default.
Exemplu:
for
Sintaxa pentru for este următoarea:
LISTA este o inșiruire de elemente separate prin spații. Variabila va lua pe rând aceste valori. Dacă se doresc variabile numerice în stilul C se poate folosi construcția (( ... )).
Exemple:
while
Sintaxa pentru while este următoarea:
Conditia are același format ca la if.
Exemplu:
Functii shell
Ca orice alt limbaj de programare, shell-ul permite lucrul cu funcții proceduri. Sintaxa unei funcții este:
Identificatorul function este optional. Sintaxa de apel este simplă: numele funcției urmat de eventualii parametri.
ATENTIE! In cadrul unei funcții argumentele vor fi referite tot ca parametri pozitionali ($1, $2, ...) astfel incât dacă dorim să referim într-o funcție argumentele în linia de comandă va trebui să-i transmitem ca parametri la apelul funcției.
Exemplu:
Pattern matching
In interactiunea cu sistemul de fișiere se dorește selectarea rapidă a mai multor fișiere după câteva caracteristici de nume comune. Operația efectuată de shell se numește pattern matching. Există următoarele caractere speciale:
'*' - se potriveste cu orice șir de caracter, inclusiv șirul vid
? - se potriveste cu un singur caracter
[...] - se potriveste cu unul din caracterele din set; poate fi de genul [abc] sau [a-gA-G] sau [0-5h-zH-Z]
Pentru următoarele pattern-uri trebuie activată optiunea shopt -s extglob.
{sir1,sir2,sir3,..} - se potriveste cu unul dintre sirurile dintre acolade
?(lista_sabloane) - se potriveste cu o aparitie sau nici una a unuia dintre sabloane
*(lista_sabloane) - se potriveste cu nici o aparitie sau mai multe a unuia dintre multe sabloane
+(lista_sabloane) - se potriveste cu o aparitie sau mai multe a unuia dintre sabloane
@(lista_sabloane) - se potriveste cu exact un sablon din lista
!(lista_sabloane) - se potriveste cu toate sabloanele din lista mai putin unul
Exemplu:
Comenzi utile
O serie de comenzi (interne sau externe) sunt utile în crearea de scripturi shell.
echo
Comanda echo este folosită pentru a afișa un șir de caractere, o variabilă la ieșire standard:
cat, tac, head, tail
Comanda cat afișează conţinutul unui fișier sau al intrării standard. Comanda tac afișează conţinutul unui fișier inversat. Comanda head afișează începutul unui fișier sau al intrării standard, în timp ce comanda tail afișează sfârșitul acestuia:
read
Comanda read este folosită pentru citirea de informaţii de la intrarea standard:
O folosire frecventă este în combinaţie cu while pentru citirea linie cu linie a conţinutului unui fișier:
find
Comanda find este o comandă fundamentală pentru parcurgerea intrărilor dintr-o ierarhie a sistemului de fișiere. Câteva exemple de utilizare sunt prezentate în continuare:
cautarea headerelor de sistem care încep cu șirul std:
căutarea șirului mutex în headerele de sistem care conţin șirul lock
Opţiunea exec este folosită pentru a rula o comandă pentru fișierele găsite. Șirul {} este special și se înlocuiește cu numele fișierului găsit de find. Caracterul ; (citat cu ajutorul backslash) indică încheierea comenzii de executat.
dd
Utilitarul dd din Linux poate fi folosit pentru a:
Clona un disc
Clona o partiție
Face a backup și a restaura întregul hard disk sau partiție
Șterge conținutul hard diskului
syntaxa:
Mai întâi, se execută comanda lsblk pentru a vizualiza toate discurile disponibile pe sistem, sau fdisk -l.
Pentru a clona un întreg disc /dev/sdb în /dev/sdc, vom folosi următoarea comandă:
De exemplu, pentru a clona o partiție /dev/sdb2 în /dev/sdc2, comanda ar fi:
Următoarea comandă dd elimină datele din /dev/hdX:
Filtre de text
Variabilele, structurile de control și procedurile sunt întâlnite în toate limbajele de programare. Ce face însă un script shell indicat pentru sarcini administrative și repetitive este posibilitatea de îmbinare a comenzilor simple, de lucru cu fișierele sistemului pentru a obține informatiile dorite și pentru a adăuga o nouă funcționalitate. De obicei aceste sarcini necesită o procesare sofisticată. În aceste situații se folosesc filtrele de text.
Comenzi folosite ca și filtre de text sunt head, tail, grep, sort, uniq, tr, cut.
head, tail
Cele două comenzi sunt folosite pentru afișarea numai a primelor sau a ultimelor linii de text din cadrul unui fișier. Sintaxa lor este asemănătoare:
Primul argument, dacă există, afișează primele, respectiv ultimele n linii din text. Lipsa acestuia impune n = 10.
Exemple de comenzi sunt:
În cazul comenzi tail o opțiune utilă este -f care menține fișierul de vizualizat deschis pe masura ce programele scriu in el, spre exemplu pentru a vizualiza un fișier de log pe măsura ce informațiile sunt scrise în el:
grep
Comanda grep permite localizarea liniilor intr-un fișier care conține o expresie căutată. Sintaxa de baza este
file este numele fișierului în care vrem să gasim cuvântul word. Un exemplu de utilizare este:
De multe ori dorim să realizăm căutarea în mod case-insensitive (adică să nu conteze faptul ca se caută UNIX sau unix). Pentru aceasta folosim optiunea -i.
Când nu se precizează un fișier se folosește intrarea standard, grep devenind ideal pentru lucrul cu pipes. De exemplu:
O altă opțiune utilă este -v. Aceasta permite căutarea acelor linii care NU conțin cuvantul transmis ca parametru. Spre exemplu, dacă dorim afișarea utilizatorilor care nu au ca și director de baza un director de forma /home/nume, vom folosi comanda:
Un alt exemplu de folosire este parsarea output-ului comenzii ps:
Optiunea -n permite afisarea numarului liniei care continea cuvantul cautat.
Putem de asemenea să afișăm numai fișierele care conțin acel cuvânt (fără afișarea liniilor). Pentru aceasta folosim opțiunea -l (listare). De obicei este folosită în conjuncție cu opțiunea -R pentru căutarea recursivă in cadrul unei structuri de directoare:
tr
Comanda tr (transliterate) este folosita pentru a translata caracterele dintr-un set de caractere intr-un alt set de caractere. Sintaxa de baza este:
Spre exemplu, dacă am dori să aflăm numărul de cuvinte dintr-un text, am translata toate caracterele speciale în spații cu o comanda de forma:
Caracterele [ si ] au fost escapate folosind . Dacă dorim să translatăm literele mari în litere mici, folosim:
In cazul în care setul set2 are mai puține caractere decât setul set1 acestea vor fi multiplicate pentru a ajunge la aceeasi dimensiune.
Următorul pas ar fi eliminarea spațiilor redundante. Opțiunea -s (squeeze) inlocuiește o succesiune de două sau mai multe caractere cu unul singur. Un exemplu este:
sort
Comanda sort este utilizată pentru sortarea liniilor alfabetic. Un exemplu de utilizare este:
O optiune utilă in cazul sort este sortarea după valoarea numerică a șirurilor. Pentru aceasta folosim -n. De exemplu:
De multe ori output-ul apare intr-o forma în care elementele de sortat sunt într-o altă coloană (nu prima, cea folosita implicit de sort). Pentru aceasta se poate folosi opțiunea -k (key). Sintaxa, în cazul folosirii acestei optiuni, este:
Aici start este coloana de start a cheii, iar end este coloana de stop a cheii. Prima coloana este cea după care se face sortarea, iar, în cazul în care sunt două sau mai multe elemente egale, se face deosebirea între acestea folosind coloana următoare din cheie, etc.
Exemplu de utilizare este:
uniq
Se pot elimina duplicatele folosind opțiunea -u la sort. Totusi, de multe ori este util să afișăm de câte ori apare un cuvânt. Pentru aceasta vom folosi comanda uniq cu optiunea -c. Atentie! uniq face eliminarea duplicatelor numai dacă liniile sunt sortate. Exemple de utilizare:
wc
Comanda wc (word count) este folosită pentru a contoriza numărul de linii, de cuvinte sau de caractere dintr-un text sau de la intrarea standard. Pentru aceasta i se pot specifica opțiunile -c, -w, -l.
cut
Comanda cut selectează numai anumite părți (coloane) ale fișierului de intrare sau ale intrării standard.
Sintaxa cea mai folosită este:
Folosindu-se delimitatorul delim se vor selecta numai câmpurile fields. Exemple de utilizare sunt:
Comenzi din aceeași categorie sunt paste și join.
Exemple
In continuare sunt prezentate câteva exemple de script-uri shell.