Comandă | Descriere scurtă |
---|---|
cat | afișare conținut fișier |
seq | generare secvență de numere |
grep | extrage linii care conțin o anumită expresie regulată |
cut | extragere anumite coloane |
sed | filtru de text avansat, folosit pentru substituții |
wc | contorizare număr de linii, cuvinte sau caractere |
head | afișare a primelor linii sau caractere |
tail | afișare a ultimelor linii sau caractere |
for | parcurgere listă de elemente pentru aplicarea de comenzi pentru fiecare element în parte |
if | condiție de execuție a unor comenzi |
$(comm) | expandarea unei comenzi (command expansion) - înlocuirea cu output-ul comenzii |
comm1 | comm2 | legarea a două comenzi: output-ul primei comenzi devine input pentru cea de-a doua comandă |
$var sau ${var} | valoarea variabilei var |
${#var} | lungimea valorii variabilei var (în caractere) |
${var:2:3} | un subșir al valorii variabilei var : de la indexul 2 se extrag 3 caractere |
Pentru acest laborator vom folosi comenzi și noțiuni prezentate în laboratoarele de până acum în special laboratorul trecut (Laborator 07 - Automatizarea în linia de comandă). Vor fi puține comenzi nou introduse; cel mai mult ne va interesa cum, dându-se o situație practică, vom putea folosi shell-ul pentru automatizare.
Automatizarea sarcinilor se referă la delegarea / degrevarea acțiunilor utilizatorilor către programe în sistemul de calcul. Un program va face automat, de sine stătător, acțiuni pe care le-ar face utilizatorul, rezultând în timp mai puțin consumat de utilizator.
Acțiunile care sunt candidate pentru automatizare sunt în general acțiuni repetitive, nu foarte simple și neinteractive. Automatizarea acestora va elibera utilizator de la executarea repetată, plictisitoare, manuală a acestora.
Automatizarea sarcinilor urmărește eficiență, în special temporală. Cu cât face mai rapid acțiuni repetitive, cu atât este mai bine. Acțiunile repetitive necesită interacțiune minimă cu utilizatorul, pentru a eficientiza timpul
Când automatizăm, folosim scripting și funcționalități existente. Un principiu esențial al automatizării este don't repeat yourself (nu reinventăm roata). Dacă există comenzi sau funcționalități existente, le folosim pe acelea. Altfel spus, urmărim atât eficiența execuției cât și eficiența dezvoltării.
Eficiența execuției o putem realiza nu doar prin scripting, ci și prin acțiuni precum:
Atunci când folosim scripting în shell, ne folosim de funcționalități existente ale shell-ului, pe care le vom descrie mai jos.
Scripting presupune folosirea unui limbaj interpretat (numit limbaj de scripting). Acesta poate fi un limbaj precum Python, Perl sau Ruby sau poate fi chiar shell-ul CLI (interpretorul de comenzi). Limbajele de scripting au avantajul dezvoltării rapide: adăugăm și testăm rapid funcționalități noi.
Shell scripting are avantajul folosirii comenzilor existente în shell (comenzi de lucru cu fișiere, cu procese, comenzi de filtrare de text). Pentru acțiuni rapide (de tipul quick'n'dirty), shell scripting este o soluție foarte bună. Atunci când avem prelucrări numerice sau prelucrări de șiruri, shell scripting poate fi insuficient și este indicat să apelăm la un limbaj de scripting mai complet, precum Python, Ruby sau Perl.
Funcționalitățile shell-ului sunt folosite în eficiența rulării comenzilor și în automatizare. Le-am folosit și amintit pe parcursul ultimelor laboratoare, în special în laboratorul trecut. Astfel de funcționalități sunt:
>
, <
, >>
|
(pipe)Pe parcursul acestui laborator vom folosi funcțioalități ale shell-ului care sunt în special folosite în shell scripting:
if
, for
, while
Automatizarea prin shell scripting presupune, în mod uzual, o secvență de comenzi. Aceste comenzi sunt adăugate într-un script unde vor fi rulate la comun, fără a fi nevoie de rularea manuală a fiecărei comenzi.
Pentru a configura rulare unei secvențe de comenzi putem condiționa rularea scriptului folosind variabile sau argumente în linia de comandă. Acestea pot genera un comportament diferit al scriptului în funcție de valorile lor.
Pe baza variabilelor, argumentelor din linia de comanda sau datelor de intrare, un script poate lua decizii condiționate. Verificarea unei condiții poate duce la un comportament sau altul.
În rularea unui script adesea vom dori ca o acțiune să țintească mai multe fișiere sau procese sau șiruri. Pentru aceasta, vom urmări folosirea de bucle pentru parcurgerea de liste de elemente.
Comenzile folosite pot primi ca argument sau la intrarea standard ieșirea altor comenzi. În shell scripting folosim funcționalități de înlănțuire a comenzilor precum command expansion sau pipe-uri ducând la rezultate mai complexe.
Expresiile regulate sunt folosite pentru căutarea/selecția anumitor intrări în fișiere text și pentru parsarea informațiilor din fișiere text. În procesarea expresiilor regulate, matching-ul se face pe fiecare linie din text. Într-o expresie regulată, anumite caractere au rol special precum caracterele de mai jos.
Caracter | Efect în expresie regulată |
---|---|
^ | început de linie |
$ | sfârșit de linie |
. | orice caracter |
* | expresia anterioară de oricâte ori, posibil niciodată |
+ | expresia anterioară de oricâte ori, cel puțin o dată |
[ajt] | orice caracter din setul de caractere a , j , t |
[^ajt] | orice caracter mai puțin caracterele a , j , t |
| | expresia anterioară sau expresia de după (una dintre cele două expresii) |
? | expresia anterioară o dată sau niciodată |
Construcție | RegExp | Glob |
. | orice caracter | caracterul . |
.* | orice caracter de oricâte ori, posibil niciodată | orice incepe cu . |
? | expresia anterioară o dată sau niciodată | orice caracter |
a+ | caracterul a de oricâte ori, cel puțin o dată | caracterul a urmat de + |
În directorul din repository aferent laboratorului există un fișier students.txt
pe care îl vom folosi ca suport pentru comenzi cu expresii regulate. Acest fișier conține o listă de studenți cu numele complet al studenților (prima coloană), grupa din care fac parte (a doua coloană) și diverse note la USO (nota finală - a treia coloană, nota la testul grilă - a patra coloană - și nota la testul practic - a cincea coloană), câmpuri separate prin caracterul tab.
Pentru căutarea și selectarea de linii din fișiere text folosim comanda grep
care folosește, la rândul ei, expresii regulate. Astfel, dacă dorim să selectăm studenții care au litera z
în numele lor, folosim comanda
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ grep 'z' students.txt GHECENCO F. Răzvan 312CC 8 8.75 4.67 MARIN N. Răzvan 312CC 5 3.5 4.2
grep
să fie plasat între apostrofuri ('
) pentru a fi escapat. În felul acesta, caracterele din expresia regulată vor fi transmise întocmai comenzii grep
și nu vor fi interpretate de shell.
Doar că expresia z
nu se potrivește (nu face match) pe litera Z
(majusculă). Pentru a selecta studenții al cărore nume conține atât litera z
, cât și Z
(majusculă) folosim expresia regulată [zZ]
și comanda
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ grep '[zZ]' students.txt PAJARCU Z.P. Raul-Constantin 312CC 2 0.5 1.7 GHECENCO F. Răzvan 312CC 8 8.75 4.67 MARIN N. Răzvan 312CC 5 3.5 4.2 ZINCULESCU C. Marius-Valentin 313CC 7 4.75 5.7
Expresia regulată [zZ]
se potrivește pe setul de caractere compus din litera z
cât și Z
(majusculă). Cu aceasta au apărut două linii noi: o linie conține studentul cu numele de familie ZINCULESCU
iar cealaltă linie conține ințialele Z.P.
ambele cu litera Z
(majusculă).
Dacă vrem să selectăm studenții al căror nume de familie începe cu litera F
, atunci vom folosi expresia regulată ^F
denotând litera F
(majusculă) la început de rând. Vom folosi, așadar, comanda de mai jos:
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ grep '^F' students.txt FLOREA N. Monica-Andreea 313CC 9 7.5 8.5 FULGER P. Alexandru-Gabriel 315CC 7 4.25 5
Dacă vrem, în schimb, să selectăm studenții al căror prenume începe cu litera F
trebuie să privim lucrurile altfel. Urmărind textul putem observa că prenumele sunt precedate fie de caracterul spațiu (blank) fie de caracterul minus (-
). Pentru început vom folosi expresia regulată [ -]F
care face match pe caracterul spațiu sau minus urmat de caracterul F
majusculă. Comanda aferentă este:
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ grep '[ -]F' students.txt ONEA I. Flavia-Katilina 311CC 7 6.5 4.33 PLEȘEA Fl. Alexandru 311CC 9 9.25 7.9 GHECENCO F. Răzvan 312CC 8 8.75 4.67 EPURE P. Andi-Florin 314CC 8 9.5 3.67 NEACȘU C. Florin-Mărgărit 314CC 10 9 9 COSTEA I. Florin Traian 315CC 4 3.5 3.7 CHIȚESCU E. Bogdan-Florentin 315CC 9 7.75 6.89
Observăm, însă, că se face match și pe șirul Fl.
și pe șirul F.
reprezentând inițiale. Știm că un prenume conține doar litere mici și că nu se încheie cu punct (.
, dot). Atunci extindem expresia regulată de mai sus la expresia [ -]F[a-z]\+[^\.]
. Expresia regulată face match pe un șir care:
-
);F
(majusculă)[a-z]
)+
)).
, dot), folosind expresia [^\.]
. Folosim backslash (\
) pentru a escapa caracterul punct (.
, dot); altfel ar fi însemnat orice caracter, așa cum înseamnă într-o expresie regulată.
+
, dar și al caracterelor ?
, {
, (
, )
și |
. Pentru a-și păstra rolul special, acestea trebuie precedate de backslash în construcții de forma \+
, \?
, \{
, \(
, \)
și \|
.
Găsiți această precizare în pagina de manual a comenzii grep
; căutați șirul Basic vs Extended
.
Astfel, comanda grep
pentru a extrage studenții al căror prenume începe cu litera F
este
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ grep '[ -]F[a-z]\+[^\.]' students.txt ONEA I. Flavia-Katilina 311CC 7 6.5 4.33 EPURE P. Andi-Florin 314CC 8 9.5 3.67 NEACȘU C. Florin-Mărgărit 314CC 10 9 9 COSTEA I. Florin Traian 315CC 4 3.5 3.7 CHIȚESCU E. Bogdan-Florentin 315CC 9 7.75 6.89
[a-zA-Z]
. Dacă ați fi dorit să faceți match pe o literă (mică sau mare) sau pe o cifră, ați fi folosit expresia [a-zA-Z0-9]
.
Utilitarul tr permite translatarea, ștergerea și manipularea caracterelor primite la intrare. Practic, sed este un stream editor ce poate efectua transformări la nivel de string asupra unui text primit la intrare. În plus, sed poate primi expresii regulate ca argument de căutare.
Spre exemplu, folosind atât tr, cât și sed, să înlocuim caracterul ',' cu TAB
în student.csv
:
student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat students.csv | tr , "\t" > students.out student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat students.out VLĂDUȚU I. Liviu-Alexandru 311CC 6 3.5 5.22 GEORGIU V. Alexandra-Maria 311CC 10 10 9.67 PĂUNOIU N. Gabriel 311CC 7 6.5 3.5 BĂCÎRCEA A. Andrei 311CC 7 5.5 4.44 [...] student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ sed 's/,/\t/g' students.csv > students.out student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat students.out VLĂDUȚU I. Liviu-Alexandru 311CC 6 3.5 5.22 GEORGIU V. Alexandra-Maria 311CC 10 10 9.67 PĂUNOIU N. Gabriel 311CC 7 6.5 3.5 BĂCÎRCEA A. Andrei 311CC 7 5.5 4.44 [...]
Observăm că cele două comenzi au același efect. Pentru a înlocui șiruri de caractere, tr nu mai oferă funcționalitatea dorită întrucât el face o mapare 1-la-1 între caracterele setul ce trebuie înlocuit și setul ce înlocuiește. În exemplul de mai jos, 1
este translatat în 2
indiferent unde apare, iar C
în A
:
student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat students.csv | tr "311CC" "312CA" > students.out student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat students.out VLĂDUȚU I. Liviu-Alexandru,322AA,6,3.5,5.22 GEORGIU V. Alexandra-Maria,322AA,20,20,9.67 PĂUNOIU N. Gabriel,322AA,7,6.5,3.5 BĂAÎRAEA A. Andrei,322AA,7,5.5,4.44 [...] student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ sed "s/311CC/312CA/g" students.csv > students.out student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat students.out VLĂDUȚU I. Liviu-Alexandru,312CA,6,3.5,5.22 GEORGIU V. Alexandra-Maria,312CA,10,10,9.67 PĂUNOIU N. Gabriel,312CA,7,6.5,3.5 BĂCÎRCEA A. Andrei,312CA,7,5.5,4.44 [...]
Am văzut în laboratoarele precedente cum putem extrage informații structurate pe linii și coloane folosind utilitarul cut.
student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ cat /etc/passwd | cut -d':' -f1,6 | head -3 root:/root daemon:/usr/sbin bin:/bin
Utilitarul awk permite aceleași acțiuni ca și cut, dar funcționalitatea sa este mai extinsă. Spre exemplu, awk poate folosi o expresie regulată ca delimitator, pe când cut acceptă un singur caracter:
student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ netstat -i Kernel Interface table Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg eth0 1500 0 1955 0 0 0 521 0 0 0 BMRU lo 65536 0 359 0 0 0 359 0 0 0 LRU student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ netstat -i | cut -d' ' -f1,2 Kernel Interface Iface eth0 lo student@ubuntu:~/uso-lab/labs/06-scripting/support/00-basics$ netstat -i | awk -F' *' '{print $1,$2,$4}' Kernel Interface Iface MTU RX-OK eth0 1500 1955 lo 65536 359
Dacă dorim să sortăm studenții din fișier în ordinea numelor vom folosi comanda sort
:
student@uso:~/uso-lab/labs/06-scripting/00-basics$ sort students.csv ALECU C. Ionuț-Gabriel,312CC,7,4.5,6.4 ASĂVOAEI P. Cătălin,315CC,8,6.75,7 BĂCÎRCEA A. Andrei,311CC,7,5.5,4.44 BADEA P. Bogdan,314CC,4,2.75,1.56 [...]
Cateva optiuni des folosite cu sort sunt:
-t
specifica separatorul-k
specifica index-ul, sau cheia, coloanei dupa care vrem sa sortam intrarilen
sortare numerică (implicit este alfabetica)-r
sortare inversa (implicit crescator)
-k 3
în loc de -k 3,3
atunci când folosim mai multe chei de sortare. Dacă folosim -k 3
atunci cheia de sortare este dată de toate coloanele începând cu a 3-a coloană. În acest caz viitoare opțiuni de chei de sortare nu mai sunt luate în considerare.
Vezi și discuția de aici.
Dorim sa facem o sortare numerica descrescatoare după notă, urmata de o sortare alfabetică după grupă (adică să fie toate notele de 10 la început dar sortate în ordinea grupelor). Astfel, pentru a sorta intrările în ordinea notei, vom folosi separatorul ,
(virgulă, comma) și coloana a 3-a pentru cheie:
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ sort -t ',' -k 3,3nr -k 2,2 students.csv GEORGIU V. Alexandra-Maria,311CC,10,10,9.67 MUȘATESCU V. Alexandru-Petrișor,311CC,10,8.5,9 RADU L. Alina,311CC,10,10,7.89 GONDOȘ I. Gabriel,312CC,10,9,7.33 [...]
În cazul în care există linii duplicate, putem folosi utilitarul uniq în conjuncție cu sort pentru a le elimina. În acest scop, adăugăm o linie duplicata în students.csv
:
VLĂDUȚU I. Liviu-Alexandru,311CC,6,3.5,5.22 GEORGIU V. Alexandra-Maria,311CC,10,10,9.67 [...] VLĂDUȚU I. Liviu-Alexandru,311CC,6,3.5,5.22 BENE D. Adrian,312CC,9,10,5 [...]
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ sort -t ',' -k 3,3nr -k 2,2 students.csv | wc -l 93 student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ sort -t ',' -k 3,3nr -k 2,2 students.csv | uniq | wc -l 92 student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ uniq students.csv | wc -l 93
Un script shell este un fișier text care conține comenzi și construcții specifice shell-ului. Un script shell începe cu construcția #!/bin/bash
, denumită shebang care indică interpretorul scriptului; în cazul de față interpretorul este chiar shell-ul Bash. Dacă nu este specificat nici un shell prin shebang atunci implicit va fi luat shell-ul implicit (setat în /etc/passwd
) asignat utilizatorului logat.
Spre exemplu, one-liners pe care le-am tot scris până acum ar putea fi puse într-un shell script și, mai mult, dacă sunt comenzi lungi și nu încap pe o linie le putem “sparge” în mai multe linii folosind separatorul \
:
#!/bin/bash export DATE=$(date +20%y%m%d) && \ mkdir -p /usr/share/snapshots && \ tar -cvpzf /usr/share/snapshots/$HOSTNAME_$DATE.tar.gz \ --exclude=/proc --exclude=/lost+found \ --exclude=/sys --exclude=/mnt \ --exclude=/media --exclude=/dev \ --exclude=/share/Archive /
\
trebuie să nu mai existe nici un caracter (nici măcar spațiu alb/trailing whitespaces)
Scriptul conține comenzi uzuale folosite în shell și alte comenzi care se regăsesc mai adesea în scripturi: while
, if
, for
. Acestea nu sunt instrucțiuni, ci sunt tot comenzi shell; pot fi folosite, dacă sintaxa este corectă și în linia de comandă.
Un script moștenește variabilele de mediu ale shell-ului părinte precum HOME
, BASH
, IFS
, USER
. În script le putem folosi astfel:
#!/bin/bash echo $HOME
Pe lângă aceste variabile, scriptul poate primi o serie de argumente din linia de comandă. Folosind următoarele variabile, putem referi argumentele primite de script din linia de comandă:
$*
este un string ($1, $2 … $n
) format din toate argumentele primite de script $@
este o listă formată din argumentele scriptului $1, $2 … $n
reprezintă fiecare parametru din script $0
este numele scriptului $#
este numărul de argumente primite din linia de comandă
Spre exemplu, să luăm următorul script arguments.sh
:
#!/bin/bash echo There are $# arguments to $0: $* echo first argument: $1 echo second argument: $2 echo third argument: $3 echo the list of arguments: $@
Să apelăm scriptul cu 4 argumente:
student@uso:~$ ./arguments.sh banane cirese caise castraveti There are 4 arguments to arguments.sh: banane cirese caise castraveti first argument: banane second argument: cirese third argument: caise the list of arguments: banane cirese caise castraveti
Creați un nou script extract-name
cu conținutul de mai jos:
#!/bin/bash IFS=',' while read name group final_grade test_grade practical_grade; do echo "$name" done < students.csv
Pentru parsare în shell folosim construcția while read ...
. Construcția este urmată de numele variabilelor în care vom reține câmpurile parsate din cadrul fiecărei linii. Folosim la intrare fișierul students.csv
aflat în directorul părinte; este un fișier în format CSV (Comma Separated Values) folosind ca separator caracterul virgulă (,
, comma). Pentru a extrage doar numele studenților din fișierul de intrare vom rula scriptul extract-name
:
student@uso:~/uso-lab/labs/06-scripting/support/00-basics$ ./extract-name VLĂDUȚU I. Liviu-Alexandru GEORGIU V. Alexandra-Maria PĂUNOIU N. Gabriel BĂCÎRCEA A. Andrei [...]
Întrucât formatul de intrare folosește virgulă (,
, comma) pe post de separator, am definit în script variabila internă IFS
(Internal Field Separator) la valoarea ','
, așa cum observăm în linia 3 din scriptul extract-name
:
IFS=','
Pașii de mai sus puteau fi realizați și cu ajutorul comenzii cut
. Dar în cazul parsării folosind construcția while read
avem două avantaje:
cut
permitea afișarea de coloane doar în ordinea din fișierul de intrare;while read
informația parsată. Spre exemplu, afișarea poate avea forma "Studentul ... face parte din grupa ...".Putem extinde script-ul de mai sus pentru a afișa doar studenții care au media mai mare decât 5.
#!/bin/bash IFS=',' while read name group final_grade test_grade practical_grade; do if test "$final_grade" -gt 5; then echo "$name,$group,$final_grade" fi done < students.csv
În bash există și construcții de tipul for
. Un exemplu comun este acela de a itera prin conținutul unui director și de a face prelucrări asupra fișierelor. Spre exemplu, dorim să facem backup tuturor fișierelor dintr-un director trimis ca parametru în script:
#!/bin/bash for file in $1/* do if test -f $file; then stat --print="%a %F %n\n" $file cp $file $file.bkp fi done
Se poate itera și pe output-ul unei comenzi:
#!/bin/bash for file in $(find /usr/share/pixmaps/ -type f -iname '*.jpg') do echo $file done
Accesați subdirectorul ~/uso-lab/labs/06-scripting/support/
pentru exercițiile de laborator. Sunt și tutoriale (deja făcute / rezolvate) și exerciții. Tutorialele au rolul de a vă acomoda cu diferitele comenzi iar exercițiile sunt parte practică efectivă.
Realizați exercițiile în ordine. De la secțiunea 7 faceți echipe de 2-3 persoane, sub coordonarea asistentului și lucrați împreună la rezolvarea exercițiilor.
Accesați subdirectorul 01-alias/
. Nu există fișiere de suport pentru aceste exerciții.
În acest set de exerciții / tutoriale vom vedea cum folosim alias-uri pentru eficientizarea acțiunilor.
Pentru început să folosim comanda xdg-open
în formele de mai jos:
xdg-open . xdg-open http://google.com xdg-open ../07-extend-scripts/convert/Orar1CA.xls
Aceste comenzi deschid respectiv, un file browser, un web browser și LibreOffice.
Pentru a realiza mai rapid acțiunile, folosim un alias. Facem alias-urile open
și go
pentru xdg-open
ca mai jos:
alias open='xdg-open' alias go='xdg-open'
Exercițiu: Acum folosiți comanda open
sau comanda go
pentru cele trei acțiuni de mai sus. Am simplificat comanda xdg-open
(mai greu de scris și mai greu de reținut).
Accesați subdirectorul 02-one-liners/
din directorul laboratorului.
Intrați în subdirectorul indexed-names/
. Dorim să realizăm operații automate în sistemul de fișiere, folosind for
și one linere.
Rulăm cele două comenzi de mai jos:
for i in $(seq -f "%02g" 1 3); do touch test-"$i".txt; done for i in $(seq -f "%02g" 0 12); do mkdir uso-curs-"$i"; done
La sfârșitul rulării acestor comenzi obținem 3 fișiere text (pentru cele 3 lucrări/teste) si 13 directoare, pentru cele 13 directoare. Am folosit for
pentru parcurgerea unei liste și seq
pentru crearea unei liste numerice.
Exercițiu: Realizați un one liner care creează cele 13 directoare și în cadrul fiecărui director creează un subdirector numit slides/
, un fișier numit note.txt
și un fișier numit resurse.txt
.
Intrați în subdirectorul rename/
. Copiați fișierele din subdirectorul src/
în directorul curent:
cp src/* .
Vrem să redenumim toate fișierele cu extensia .c
ca să avem prefixul uso_
în nume. Pentru aceasta folosim one liner-ul:
for i in *.c; do mv "$i" uso_"$i"; done
După această comandă, vor apărea fișiere redenumite.
Apoi ștergeți fișierele nou generate și recopiați-le din directorul src/
:
rm *.c cp src/*.c .
Exercițiu: Realizați un one liner care redenumește toate fișierele cu extensia .c
și adaugă sufixul _uso
în nume. Adică fișierul ptr.c
devine ptr_uso.c
.
În cadrul subdirectorului convert/
avem mai multe fișiere .svg
. Dorim să le convertim în fișiere PNG. Pentru aceasta folosim comanda inkscape
.
inkscape
va trebui să o instalați folosind comanda:
sudo apt install inkscape
Pentru a converti fișierul acl.svg
în fișierul acl.png
folosim comanda:
inkscape --export-png=acl.png acl.svg
În urma comenzii a apărut fișierul acl.png
. Verificăm conversia corectă folosind alias-ul go
creat anterior:
go acl.png
Pentru a realiza conversia tuturor fișierelor din directorul curent folosim one liner-ul:
for i in *.svg; do inkscape --export-png=$(basename "$i" .svg).png "$i"; done
În urma rulării acestui one liner, toate fișierele .svg
au fost convertite în fișiere PNG păstrându-se numele și adăugându-se extensia .png
.
Accesați subdirectorul 03-one-liners-vs-commands/
din directorul laboratorului. Vom compara one linere cu comenzi mai simple.
Rulăm următoarele comenzi pentru a genera o parolă de 16 caractere și pentru a afișa PID-urile proceselor utilizatorului student
:
tr -dc 'a-zA-Z0-9~!@#$%^&*_()+}{?></";.,[]=-' < /dev/urandom | fold -w 32 | head -n 1 ps -ef | grep student | tr -s ' ' | cut -d ' ' -f 2
Aceste acțiuni le putem realiza și folosind comenzi dedicate și opțiunile lor, fără a fi nevoie de trecerea prin operatorul |
(pipe):
pwgen -y 16 1 ps -u student -o pid --no-header
|
(pipe).
Intrați în subdirectorul indexed-names/
. Vrem să realizăm operații similare exercițiului indexed-names/
de la punctul anterior.
Astfel că, putem realiza același lucru precum comenzile de mai jos:
for i in $(seq -f "%02g" 1 3); do touch test-"$i".txt; done for i in $(seq -f "%02g" 0 12); do mkdir uso-curs-"$i"; done
folosind comenzile:
touch $(seq -f "test-%02g.txt" 1 3) mkdir $(seq -f "uso-curs-%02g" 0 12)
Avantajul celui de-al doilea set de comenzi este că vor crea un singur proces (touch
sau mkdir
). Avantajul primului set de comenzi este că putem adăuga mai multe (și mai diferite) comenzi care să fie executate pe fiecare element din bucla for.
Accesați subdirectorul 04-regex/
din directorul laboratorului. Vrem să folosim expresii regulate pentru a prelucra fișierele students.csv
și students.txt
.
Exerciții: Folosiți grep
pentru a extrage, respectiv, din fișierul students.csv
:
315CC
315CC
și care au luat prima notă 9
(din cele 3 coloane)315CC
și care au luat a doua notă care începe cu cifra 7
(din cele 3 coloane)315CC
și care au luat a treia notă care începe cu cifra 6
(din cele 3 coloane)Mai jos sunt rezultatele așteptate pentru cele patru comenzi de mai sus:
$ grep TODO students.csv ȚIFREA C. Alexandru-Nicolae,315CC,7,7,6.25 BOGDAN O.Gh. Ana,315CC,5,4.5,1.89 COSTEA I. Florin Traian,315CC,4,3.5,3.7 OLTEANU Ș. Ionuț-Mihăiță,315CC,7,4.25,4.7 FULGER P. Alexandru-Gabriel,315CC,7,4.25,5 DUȘE-VASILIU V. Andra,315CC,8,7,6.4 CHIȚESCU E. Bogdan-Florentin,315CC,9,7.75,6.89 MAVRODIN I. Andrei,315CC,9,7,6.4 DRĂGOI M. Ovidiu-Alexandru,315CC,8,8,3.44 MANEA A. Mihail-Alin,315CC,7,6.75,6.22 TRACHE M. Ana-Maria,315CC,7,4.75,4.9 IORDACHE M. Tiberiu-Constantin,315CC,8,8.25,4.56 BARBU I. Ștefan,315CC,10,10,7.56 MĂNOIU I. Ioana-Veronica,315CC,10,10,8.6 STOICA I.D. Aurel-Octavian,315CC,7,3.75,5.4 IUȘAN V. Bogdan-Cosmin,315CC,9,7.25,8 BURCEANU D.N. Radu-Gabriel,315CC,8,8.75,7 ASĂVOAEI P. Cătălin,315CC,8,6.75,7 ȘTIRBĂȚ A. Steliana,315CC,10,10,10 $ grep TODO students.csv CHIȚESCU E. Bogdan-Florentin,315CC,9,7.75,6.89 MAVRODIN I. Andrei,315CC,9,7,6.4 IUȘAN V. Bogdan-Cosmin,315CC,9,7.25,8 $ grep TODO students.csv ȚIFREA C. Alexandru-Nicolae,315CC,7,7,6.25 DUȘE-VASILIU V. Andra,315CC,8,7,6.4 CHIȚESCU E. Bogdan-Florentin,315CC,9,7.75,6.89 MAVRODIN I. Andrei,315CC,9,7,6.4 IUȘAN V. Bogdan-Cosmin,315CC,9,7.25,8 $ grep TODO students.csv ȚIFREA C. Alexandru-Nicolae,315CC,7,7,6.25 DUȘE-VASILIU V. Andra,315CC,8,7,6.4 CHIȚESCU E. Bogdan-Florentin,315CC,9,7.75,6.89 MAVRODIN I. Andrei,315CC,9,7,6.4 MANEA A. Mihail-Alin,315CC,7,6.75,6.22
Accesați subdirectorul 05-find/
din directorul laboratorului.
Pentru început vom crea o ierarhie de fișiere pe care să o folosim în continuare folosind comanda:
tar xf etc.tar.gz
În urma rulării acestei comenzi obținem un subdirector etc/
. Folosim comanda find
pentru a parcurge fișiere din ierarhia de fișiere etc/
.
Pentru început rulăm comenzile de mai jos pentru a afișa anumite intrări:
find etc/ find etc/ -type f find etc/ -type f -name 'a*' find etc/ -type f -name 'a*.*'
Exercițiu: Afișați intrările de tip link simbolic din ieraria etc/
. Apoi afișați intrările de tip link simbolic care încep cu litera a
.
Dacă dorim să rulăm anumite informații despre un fișier dat, precum dimensiunea sa, putem folosi comenzi precum cele de mai jos:
find etc/ -type f -exec stat -c "%s" {} \; find etc/ -type f -exec stat -c "%s,%n" {} \; find etc/ -type f -exec stat -c "%s,%n" {} \; | sort -n find etc/ -type f -exec stat -c "%s,%n" {} \; | sort -n | tail -10
Exercițiu: Ștergeți toate intrările de tip link simbolic din ierarhia etc/
al căror nume începe cu litera a
.
Putem selecta anumite fișiere după permisiunile prezente. De exemplu, pentru a afișa fișierele care au permisiuni de execuție pentru toți utilizatorii (adică bitul x
este prezent pentru user
, group
și others
) folosim comanda:
find etc/ -type f -perm /111 find etc/ -type f -perm /111 -exec ls -l {} \;
Exercițiu: Scoațeți permisiunile de execuție de pe toate fișierele care au permisiuni de execuție pentru toți utilizatorii.
Putem selecta fișiere după timpul de access, precum comenzile de mai jos:
find etc/ -type f -mtime -25 find etc/ -type f -newer etc/apt/trusted.gpg
Exercițiu: Afișați toate fișierele cu dimensiune 0
. Apoi toate fișierele cu dimensiune mai mare de 20
kiloocteți. Folosiți opțiunea -size
a comenzii find
(puteți vedea detalii în pagina de manual).
Accesați subdirectorul 06-simple-scripts/
din directorul laboratorului. În acest director avem exemple de scripturi practice. Urmăriți toate scripturile din acest director (nu e nevoie să le rulați) și vedeți ce face fiecare.
Exercițiu: Pentru dezactivarea montării stick-urilor USB pe sistem putem folosi comenzile:
sudo rmmod uas sudo rmmod usb_storage
Apoi, pentru a reactiva, folosim comenzile:
sudo modprobe usb_storage sudo modprobe uas
Creați, respectiv, scripturile disable-usb
și enable-usb
care să dezactiveze și reactiveze montarea stick-urilor USB. Rulați-le pentru verificare.
Exercițiu: Creați un script numit system-info
care afișează informații despre sistem pe modelul de mai jos:
date: 2019-11-05 kernel: 5.0.0-32-generic version: Ubuntu 18.04.3 LTS num_processes: 336
Accesați subdirectorul 07-extend-scripts/
din directorul laboratorului.
Intrați în subdirectorul convert/
. Avem 4 fișiere .xls
reprezentând orarele de anul 1. Convertim aceste fișiere în format PDF folosind scriptul solution
:
./solution
Vom obține câte un fișier PDF. Putem folosi aliasul go
pentru a deschide un fișier:
go Orar1CA.pdf
Intrați în directorul pdf-stitch/
. Vrem să obținem fișierele de tip orar; pentru aceasta folosim scriptul download
:
./download
Apoi rulați scriptul anterior pentru a converti fișierele .xls
descărcate în fișiere PDF:
../convert/solution
Ca să unim mai multe fișiere PDF folosim comanda pdftk
. De exemplu, dacă vrem să unim fișierele de anul 1 CTI folosim comanda:
mkdir out pdftk Orar1*.pdf cat output out/Orar1.pdf
Acum în fișierul out/Orar1.pdf
avem toate orarele unificate și e mai ușor de printat. Putem verifica folosind aliasul go
:
go out/Orar1.pdf
Exercițiu: Creați un script solution
care generează 6 fișiere PDF pentru orarele pentru fiecare dintre cei 6 ani (4 licență și 2 master).
Intrați în directorul wiki-cat/
. Avem mai multe directoare cu fișiere de tip wiki pe care dorim să le concatenăm.
Ca să obținem un fișier de tip wiki pentru laboratorul 1, folosim comanda:
cat 01-fs/{concepts,demo,recap,basics,need-to-know,nice-to-know,get-a-life,summary}.wiki > 01-fs.wiki
Exercițiu: Creați un script do-all
care generează fișiere de tip wiki pentru toate cele 4 laboratoare din director. Folosiți for
pentru a parcurge toate 4 directoarele.
Intrați în directorul diacritics/
. În directorul in/
avem fișiere care conțin diacritice în format necorespunzător (cedilla below); vrem să fie în format corespunzător (comma below), așa cum sunt în directorul expected/
. Lista completă de diacritice este în fișierul diacritice_ro.txt
.
Dacă doriți să vedeți codul UTF-8 pentru diacritice folosiți comanda:
xxd diacritice_ro.txt
În scriptul solution
aveți comanda sed
pentru a converti diacritica ş
(cedilla below) în diacritice ș
(comma below).
Exercițiu: Extindenți scriptul solution
pentru a converti și celelalte trei diacritice: ţ
, Ş
și Ţ
.
Exercițiu: Creați un script do-all
care convertește toate fișierele din directorul in/
în diacriticele de tip comma below. Folosiți for
pentru a parcurge toate fișierele. Pentru verificare folosiți comanda cmp
și comparați fișierele obținute cu cele din directorul expected/
.
Accesați subdirectorul 08-sample-real-scripts/
din directorul laboratorului. Aici sunt exemple de scripturi pe care le folosim în practică. Parcurgeți scripturile și înțelegeți-le. Acolo unde sunt lucruri neclare, apelați la asistent.
Accesați subdirectorul 09-args/
din directorul laboratorului.
Intrați în directorul timetable/
. Scriptul download
descarcă orarele din anul universitar curent, semestrul 1. Rulați scriptul pentru a-l testa:
./download
Va descărca fișierele pentru orar în format .xls
.
Exercițiu: Creați un script numit solution
care primește două argumente: primul este anul universitar, altul este semestrul și descarcă orarele pentru acel an universitar și pentru acel semestru de pe site-ul de orare: http://acs.pub.ro/~cpop/
Rularea fără argumente a scriptului solution
ar trebui să afișeze un mesaj de forma:
$ ./solution Usage: ./solution <academic-year> <semester> Example: ./solution 2017_2018 sem1
Intrați în directorul average/
.
Scriptul compute
calculează media la a doua notă pentru toți studenții din fișierul ../../04-regex/students.csv
:
$ ./compute Average: 6.72
Exercițiu: Creați un script numit solution
care primește ca argument un nume de grupă și calculează media doar pentru aceștia. Un exemplu de rulare este
$ ./solution 314CC Average: 7.56
Accesați subdirectorul 10-replace/
din directorul laboratorului.
Fișierul template_reader.c
conține template-ul unui fișier C. În acest fișier construcția TEMPLATE
trebuie înlocuită cu un “mesaj secret”. Acest mesaj este generat dintr-un șir în care fiecare literă este înlocuită cu valoarea sa ASCII din care scădem valoarea 0x41
(în hexazecimal). Pentru a face înlocuirea folosim scriptul generate
:
./generate
În urma rulării scriptului generate
a fost creat fișierul reader.c
pornind de la fișierul template_reader.c
. Acum putem compila fișierul folosind fișierul Makefile
cu ajutorul comenzii:
make
În urma rulării comenzii make
a rezultat executabilul reader
pe care îl putem acum lansa:
./reader
Parcurgeți scriptul generate
și înțelegeți-l. Acolo unde aveți nelămuriri, adresați-vă asistentului.
Accesați subdirectorul 11-practical/
din directorul laboratorului.
În fiecare dintre cele 3 subdirectoare aveți niște provocări. Fiecare provocare are un fișier flag
care conține un mesaj ascuns. Va trebuie să “descifrați” acel mesaj. Ca să întelegeți cum a fost generat acel mesaj ascuns, vedeți scriptul encode
. Scriptul encode este scriptul folosit pentru generarea fișierului flag
pornind de la un mesaj inițial (pe care trebuie să îl aflați).
Exercițiu: Alegeți o provocare (sau mai multe) și creați un script decode
care decodifică mesajul ascuns în fișierul flag
și afișează mesajul inițial.
Exercițiu: Gândiți-vă voi la o provocare și creați pentru aceasta scriptul encode
și decode
și fișierele auxiliare.
Accesați subdirectorul 12-shell-vs-python/
din directorul laboratorului. Vrem să vedem cum se compară scripturi scrise în shell față de scripturi scrise în Python.
Scriptul generate
este un script shell pe care l-am folosit în secțiunea 10. Scriptul generate.py
are aceeași funcționalitate, dar scris în Python, iar mesajul inițial este citit din fișieru in
.