04. Linia de comandă
În al treilea laborator ați învățat să redirectați ce citește și ce scrie o comandă și să inspectați procesele din sistem. Acum ne uităm mai atent la shell și la terminalul în care rulează. Porniți comenzi în background, le opriți, le aduceți înapoi și le trimiteți semnale. Definiți variabile și vedeți care dintre ele ajung la comenzile pe care le porniți. La final aflați exact ce face shell-ul cu o linie de text înainte să pornească o comandă: cum o împarte în cuvinte, ce fac ghilimelele și globurile și cum găsește programul de rulat.
Ideea cea mai importantă a laboratorului este că o comandă nu vede niciodată ce tastați. Shell-ul citește linia, înlocuiește variabilele și globurile, elimină ghilimelele, pregătește redirectările, găsește programul și abia apoi îl pornește, cu o listă de cuvinte drept parametri.
Obiective
- Să înțelegeți ce sunt un terminal, un pseudoterminal și un shell și să folosiți terminalele virtuale
- Să trimiteți semnale proceselor cu comanda
killși de la tastatură și să tratați un semnal cutrap - Să rulați comenzi în background și să le gestionați ca job-uri cu
&,jobs,fg,bgșikill %N - Să vedeți cum sunt grupate procesele unui terminal în grupuri de procese și sesiuni și să lăsați o comandă să
ruleze și după ce se închide terminalul, cu
nohup - Să folosiți variabile de shell și variabile de mediu, cu
export,unset,printenvșienv, precum și parametrii speciali$?,$$și$! - Să împărțiți o linie de comandă în părțile ei: atribuirile, comanda, parametrii și redirectările
- Să folosiți ghilimelele și
\ca să controlați cum împarte shell-ul cuvintele și cum înlocuiește variabilele - Să selectați fișiere cu globuri:
*,?,[...],[!...],**și{...} - Să deosebiți comenzile interne de cele externe cu
typeși să înțelegeți cum caută shell-ul înPATH
Resurse
- Razvan Deaconescu, Razvan Rughinis, Mihai Carabas, Alexandru Radovici, Utilizarea Sistemelor de
Operare, Printech 2021
- Capitolul 4 - Procese, secțiunile 4.3.3, 4.5.2 și 4.6.1
- Capitolul 7 - Interfața în linia de comandă, secțiunile 7.1 și 7.2.3 - 7.2.5
- Capitolul 9 - Pornirea sistemului, secțiunea 9.6.1
- Brian Ward, How LINUX Works, 3rd Edition, No Starch Press, 2021
- Capitolul 2 - Basic Commands and Directory Hierarchy, secțiunile 2.1, 2.2, 2.4.2, 2.8 - 2.10 și 2.16
- Capitolul 3 - Devices, secțiunea 3.4
- Capitolul 11 - Introduction to Shell Scripts, secțiunea 11.2
- Slide-urile cursului: 04. Interfața în linia de comandă
- Paginile de manual:
man bash(căutațiQUOTING,EXPANSION,Pathname Expansion,JOB CONTROL,SIGNALSșiShell Variables),man 7 signal,man kill,man nohup,man env,man printenv,man ttyșiman ps
Terminale
Pe vremuri, un calculator era folosit în comun de mulți utilizatori. Fiecare avea un terminal: o tastatură și un ecran (sau o imprimantă), fără procesor, legate printr-un cablu de calculator. Aceste terminale erau TeleTypes (teleimprimatoare), de aceea Linux încă numește un terminal TTY.

Terminalele doar trimiteau tastele către calculator și afișau ce primeau înapoi: toate programele rulau pe mainframe.
Astăzi, nucleul îi dă fiecărui program care rulează într-un terminal un dispozitiv de terminal, un fișier special
din /dev. Programul citește tastele din el și scrie textul în el, prin descriptorii de fișier 0, 1 și 2.
| Dispozitiv | Ce este |
|---|---|
/dev/tty1 ... /dev/tty6 | terminalele virtuale ale nucleului: fiecare folosește ecranul și tastatura calculatorului, pe rând |
/dev/pts/0, /dev/pts/1, ... | pseudoterminalele: câte unul pentru fiecare fereastră sau tab de terminal și pentru fiecare conexiune SSH |
/dev/tty | întotdeauna terminalul procesului care îl deschide |
Terminale virtuale
Un calculator de azi are un singur ecran și o singură tastatură, dar nucleul le face să arate ca mai multe terminale: terminalele virtuale (virtual terminals). Fiecare funcționează ca unul dintre terminalele de odinioară, cu propria autentificare și propriile programe, însă pe ecran apare doar unul la un moment dat.

În imaginea de mai sus, pe /dev/tty1, agetty a afișat promptul de autentificare, iar după autentificare shell-ul
lui alice, bash, folosește același terminal. Și interfața grafică rulează într-unul dintre terminalele virtuale, de
obicei în primul sau în al doilea. Înainte să plecați din ea, aflați în care, ca să știți cum vă întoarceți. Rulați
într-un terminal din Sway:
[student@fedora ~]$ loginctl show-session $XDG_SESSION_ID -p VTNr
VTNr=2
loginctl îi cere lui systemd informații despre sesiunea voastră: în cazul de față, Sway rulează în al doilea
terminal virtual, /dev/tty2. Ctrl+Alt+F<n> comută pe terminalul virtual n:
| Combinație de taste | Unde ajungeți |
|---|---|
| Ctrl+Alt+F3 ... F6 | un terminal în mod text, /dev/tty3 ... /dev/tty6 |
| Ctrl+Alt+F2 | înapoi în Sway, dacă VTNr este 2 (F1 dacă este 1) |
Într-un terminal în mod text, un program numit agetty așteaptă un utilizator. Autentificați-vă cu utilizatorul și
parola: login le verifică și pornește shell-ul, bash.
fedora login: student
Password:
[student@fedora ~]$ tty
/dev/tty3
[student@fedora ~]$ exit
ttya afișat numele terminalului shell-ului: de data aceasta un terminal virtual,/dev/tty3.exita închis shell-ul, iaragettyașteaptă o nouă autentificare.- Ctrl+Alt+F2 vă duce înapoi în Sway, cu toate ferestrele exact cum le-ați lăsat: Sway a rulat în continuare cât ați lipsit.
Într-o mașină virtuală VirtualBox, Ctrl+Alt+F3 este preluată de sistemul de operare gazdă, nu de mașina virtuală. Folosiți în schimb Host+F3 (tasta Host este de obicei Ctrl din dreapta) și Host+F2 ca să reveniți. Alte mașini virtuale au un meniu din care trimiteți aceste taste.
Dacă nu știți unde este Sway, încercați Ctrl+Alt+F1 și F2. Una dintre ele vă duce în Sway; cealaltă afișează un ecran de autentificare sau un terminal text.
Emulatoare de terminal
Interfața grafică, Xorg sau Wayland (Sway este un compozitor Wayland), preia unul dintre terminalele virtuale și
desenează pe ecran prin framebuffer-ul nucleului. Aici, terminalul este o aplicație grafică, un emulator de
terminal (terminal emulator): foot, kitty, ptyxis, alacritty, ghostty, ... Fiecare fereastră a unui
emulator de terminal îi cere nucleului un pseudoterminal (pseudo terminal): deschide /dev/ptmx, iar nucleul
creează un nou /dev/pts/<n>.

Un pseudoterminal are două capete. Emulatorul de terminal păstrează un capăt, master-ul, pe care îl citește și îl
scrie ca pe un fișier. Celălalt capăt, slave-ul, este fișierul /dev/pts/<n> și se comportă exact ca un terminal
adevărat. Când pornește shell-ul, emulatorul redirectează /dev/pts/<n> ca 0, 1 și 2 ale shell-ului.

Emulatorul scrie în master tastele pe care le apăsați, iar shell-ul le citește din slave. Ce scrie shell-ul în slave iese prin master, iar emulatorul desenează în fereastra lui. Shell-ul nu știe că există o fereastră: el vede doar un terminal.
[student@fedora ~]$ tty
/dev/pts/0
[student@fedora ~]$ ps
PID TTY TIME CMD
2817 pts/0 00:00:00 bash
3104 pts/0 00:00:00 ps
[student@fedora ~]$ ls /dev/pts
0 1 ptmx
[student@fedora ~]$ ls -l /proc/$$/fd/0
lrwx------. 1 student student 64 Oct 20 10:02 /proc/2817/fd/0 -> /dev/pts/0
- Shell-ul rulează în prima fereastră de terminal,
/dev/pts/0, iarpsa afișat doar procesele acestui terminal. - Sunt deschise două ferestre, deci există două pseudoterminale,
0și1. - Intrarea standard a shell-ului este pseudoterminalul, cum ați văzut în laboratorul 03.
Un pseudoterminal este un fișier: ce scrieți în el apare în fereastra terminalului respectiv. Cu două terminale
deschise, echo hello > /dev/pts/1 rulat în primul scrie hello în al doilea.
Semnale
Un semnal (signal) este un mesaj scurt pe care sistemul de operare i-l trimite unui proces: un număr, cu un
nume care începe cu SIG. Când sosește un semnal, procesul se oprește din ce făcea și execută acțiunea implicită a
semnalului sau, dacă are una, propria rutină de tratare a semnalului (signal handler), apoi își continuă
treaba.
| Acțiune implicită | Ce se întâmplă cu procesul |
|---|---|
| Term | se încheie |
| Core | se încheie și lasă o copie a memoriei sale (un core dump), pentru depanare |
| Stop | este pus pe pauză, până primește SIGCONT |
| Cont | își continuă execuția, dacă era oprit; altfel nu se întâmplă nimic |
| Ign | nimic, semnalul este ignorat |
Pe Fedora, fișierele core dump nu apar în directorul curent: le păstrează systemd-coredump. coredumpctl list le
afișează, cel mai nou la final.
Semnalele pe care le veți folosi cel mai des:
| Număr | Semnal | Acțiune implicită | Se trimite când | Poate fi tratat? |
|---|---|---|---|---|
1 | SIGHUP | Term | s-a închis terminalul (hang up) | da |
2 | SIGINT | Term | ați apăsat Ctrl+C (interrupt) | da |
3 | SIGQUIT | Core | ați apăsat Ctrl+\ | da |
9 | SIGKILL | Term | cineva vrea ca procesul să se încheie acum | nu |
15 | SIGTERM | Term | cineva îi cere procesului să se încheie; semnalul implicit al lui kill | da |
18 | SIGCONT | Cont | fg sau bg continuă un job oprit | da, dar procesul continuă oricum |
19 | SIGSTOP | Stop | cineva vrea ca procesul să se oprească acum | nu |
20 | SIGTSTP | Stop | ați apăsat Ctrl+Z | da |
21 | SIGTTIN | Stop | un proces din background a încercat să citească din terminal | da |
Numerele de la 18 la 21 sunt valabile pe calculatoarele x86 și ARM; alte procesoare folosesc alte numere. În
comenzi folosiți numele, kill -CONT, nu kill -18: ele merg peste tot. Lista completă se găsește în
man 7 signal.
Trimiterea unui semnal: kill
În ciuda numelui, kill trimite un semnal. Doar SIGTERM, semnalul implicit, îi cere procesului să se încheie.
| Comandă | Ce face |
|---|---|
kill -l | Listează toate semnalele, cu numerele lor |
kill -l <NUMBER> | Afișează numele unui semnal: kill -l 15 afișează TERM |
kill <PID> | Îi trimite procesului SIGTERM, semnalul implicit |
kill -KILL <PID> | Trimite SIGKILL; kill -9 <PID> și kill -SIGKILL <PID> fac același lucru |
kill -STOP <PID> | Oprește procesul |
kill -CONT <PID> | Continuă procesul oprit |
kill <PID1> <PID2> ... | Trimite semnalul (SIGTERM sau cel pe care îl dați) mai multor procese |
kill %<N> | Trimite semnalul job-ului N al acestui shell (vedeți job-urile) |
Pornim sleep 1000 în al doilea terminal și îi trimitem semnale din primul:
[student@fedora ~]$ pgrep sleep
3150
[student@fedora ~]$ kill -STOP 3150
[student@fedora ~]$ ps -o pid,stat,cmd -p 3150
PID STAT CMD
3150 T+ sleep 1000
[student@fedora ~]$ kill -CONT 3150
[student@fedora ~]$ ps -o pid,stat,cmd -p 3150
PID STAT CMD
3150 S+ sleep 1000
[student@fedora ~]$ kill 3150
[student@fedora ~]$ ps -o pid,stat,cmd -p 3150
PID STAT CMD
SIGSTOPa pussleeppe pauză: starea lui a devenitT(oprit).SIGCONTl-a făcut să continue: așteaptă din nou (S).SIGTERMl-a încheiat:psnu mai găsește niciun proces cu acest PID, iar al doilea terminal afișeazăTerminated.
Folosiți kill -KILL doar când kill simplu nu merge. SIGTERM îi lasă programului timp să facă ordine înainte să se
încheie (să-și salveze fișierele, să-și șteargă fișierele temporare), pe când SIGKILL nu-i dă nicio șansă.
Taste care trimit semnale
Terminalul transformă unele taste în semnale pentru procesul care rulează în foreground (în prim-plan), adică cel care citește tastatura:
| Combinație de taste | Trimite | Ce se întâmplă, implicit |
|---|---|---|
| Ctrl+C | SIGINT | job-ul din foreground se încheie |
| Ctrl+\ | SIGQUIT | job-ul din foreground se încheie, iar sistemul salvează un core dump |
| Ctrl+Z | SIGTSTP | job-ul din foreground este oprit, iar shell-ul afișează promptul (vedeți mai jos) |
| Ctrl+D | niciun semnal | la începutul unei linii, sfârșit de fișier (end of file): programul nu mai citește nimic |
[student@fedora ~]$ sleep 100
^C
[student@fedora ~]$ sleep 100
^\Quit (core dumped)
[student@fedora ~]$ sleep 100
^Z
[1]+ Stopped sleep 100
Ctrl+D la promptul shell-ului înseamnă sfârșitul intrării lui: shell-ul se încheie, iar fereastra terminalului se închide.
Tratarea unui semnal: trap
Un program poate înlocui acțiunea implicită a unui semnal cu propria rutină de tratare: o bucată din codul lui care
rulează la sosirea semnalului, în locul acțiunii implicite. Un script bash definește o rutină de tratare cu trap,
care rulează o comandă sau o funcție. Creați fișierul ~/lab04/stubborn.sh cu nano:
#!/bin/bash
# the signal handler
function no_interruptions_please () {
echo "No interruptions please"
}
# register the signal handler
trap no_interruptions_please SIGINT
while true; do
echo "working"
sleep 1
done
[student@fedora lab04]$ bash stubborn.sh
working
working
^CNo interruptions please
working
^CNo interruptions please
working
^\Quit (core dumped)
- Scriptul a scris
workingîn fiecare secundă, la nesfârșit. - Ctrl+C a trimis
SIGINT: scriptul și-a rulat rutina de tratare, a afișat mesajul și a continuat să lucreze. - Ctrl+\ a trimis
SIGQUIT, pentru care nu există o rutină de tratare: scriptul s-a încheiat.
| Comandă | Ce face |
|---|---|
trap '<command>' <SIGNAL> | Rulează command când sosește SIGNAL; ghilimelele fac din ea un singur cuvânt |
trap '' <SIGNAL> | Ignoră semnalul |
trap - <SIGNAL> | Revine la acțiunea implicită |
trap -p | Afișează rutinele de tratare |
trap păstrează rutina de tratare ca text, iar shell-ul rulează acest text la sosirea semnalului, ca și cum l-ați
tasta atunci. Ghilimelele decid când se înlocuiește o variabilă din el: între ghilimele simple, abia la sosirea
semnalului; între ghilimele duble, imediat, când rulați trap.
SIGKILL și SIGSTOP nu pot fi tratate sau ignorate: trap nu le poate schimba. De aceea kill -KILL încheie
întotdeauna un proces.
Foreground și background
Un terminal are un singur job în foreground: acesta citește tastatura și primește semnalele tastelor, iar shell-ul așteaptă să se încheie înainte să afișeze din nou promptul. Toate comenzile rulate până acum au rulat în foreground.
Shell-ul poate porni comenzi și în background (în fundal). Acestea se numesc job-uri: shell-ul nu le așteaptă și afișează imediat promptul, așa că ele rulează în același timp cu comenzile pe care le tastați. Un job este fie în rulare (running), fie oprit (stopped).

O comandă pornește în foreground (cmd) sau în background (cmd &). Ctrl+Z oprește procesul
din foreground și îl mută în background, bg continuă în background un job oprit, iar fg aduce un job înapoi în
foreground. Restul secțiunii le arată pe rând.
Pornirea unui job: &
Puneți & la finalul unei comenzi ca s-o rulați în background:
[student@fedora ~]$ sleep 100 &
[1] 4242
[student@fedora ~]$ sleep 200 &
[2] 4250
[student@fedora ~]$ jobs
[1]- Running sleep 100 &
[2]+ Running sleep 200 &
[student@fedora ~]$ echo $!
4250
[student@fedora ~]$ kill %1
[student@fedora ~]$
[1]- Terminated sleep 100
[1]este ID-ul job-ului (job ID), care există doar în acest shell;4242este PID-ul, valabil în tot sistemul.jobsa listat job-urile acestui shell:+marchează job-ul curent (ultimul), iar-pe cel dinainte.$!este PID-ul ultimului job pornit cu&.%1înseamnă job-ul 1:kill %1i-a trimisSIGTERM, iar shell-ul a anunțat că s-a încheiat când a afișat următorul prompt.
| Comandă | Ce face |
|---|---|
command & | Rulează command în background, ca job |
jobs | Listează job-urile acestui shell |
jobs -l | Le listează împreună cu PID-urile lor |
fg %<N> | Aduce job-ul N în foreground (și îl continuă, dacă este oprit) |
bg %<N> | Continuă în background job-ul oprit N |
kill %<N> | Trimite SIGTERM job-ului N |
fg, bg | Fără %<N>, lucrează cu job-ul curent, cel marcat cu + |
Un job scrie tot în același terminal, așa că rezultatul lui se amestecă cu comenzile pe care le tastați. Redirectați-i
ieșirea, de exemplu find / -name passwd > found.txt 2> /dev/null &.
Oprire și continuare: fg și bg
O comandă care rulează în foreground poate fi mutată ulterior în background:
- apăsați Ctrl+Z: comanda se oprește (
SIGTSTP), iar shell-ul îi afișează ID-ul de job, de exemplu[1]+ Stopped, și afișează promptul; - aflați ID-ul job-ului cu
jobs; - rulați
bg %<N>: job-ul își continuă execuția (SIGCONT) în background.
fg %<N> îl aduce înapoi în foreground.
[student@fedora ~]$ sleep 100 > /dev/null
^Z
[1]+ Stopped sleep 100 > /dev/null
[student@fedora ~]$ jobs
[1]+ Stopped sleep 100 > /dev/null
[student@fedora ~]$ bg %1
[1]+ sleep 100 > /dev/null &
[student@fedora ~]$ jobs
[1]+ Running sleep 100 > /dev/null &
[student@fedora ~]$ fg %1
sleep 100 > /dev/null
^C
- După Ctrl+Z,
sleeps-a oprit, iar shell-ul a afișat promptul. bgl-a făcut să continue în background (shell-ul l-a afișat cu un&la final).fgl-a adus înapoi în foreground: shell-ul îl așteaptă din nou, iar Ctrl+C ajunge la el.
Ctrl+Z vă ajută când ați uitat & sau când vreți să lăsați puțin deoparte un program, de exemplu
nano sau less, să rulați o comandă și apoi să vă întoarceți cu fg.
Job-uri care citesc tastatura
Doar procesul din foreground poate citi tastatura. Un job care încearcă s-o citească primește SIGTTIN și este
oprit:
[student@fedora ~]$ cat &
[1] 4321
[student@fedora ~]$
[1]+ Stopped cat
[student@fedora ~]$ fg
cat
hello
hello
^D
Fără parametri, cat citește tastatura, așa că s-a oprit imediat. fg l-a adus în foreground, unde poate citi: a
afișat înapoi fiecare linie, până la Ctrl+D.
Un shell care mai are job-uri oprite nu se închide din prima: exit afișează There are stopped jobs. și abia al
doilea exit îl închide (și încheie job-urile).
Sesiuni și grupuri de procese
Nucleul grupează procesele unui terminal pe două niveluri:
| Grup | Ce este | ID-ul lui: PID-ul liderului | Liderul |
|---|---|---|---|
| grup de procese (process group) | un job: toate procesele unei linii de comandă; sleep 100 | cat este un grup de două procese | PGID: primul proces din linia de comandă | liderul grupului |
| sesiune (session) | toate grupurile de procese ale unui terminal | SID, PID-ul shell-ului | liderul sesiunii: shell-ul, s în STAT |
Terminalul este terminalul de control (controlling terminal) al sesiunii. Intrarea lui și semnalele tastelor
(Ctrl+C, Ctrl+Z, ...) merg la grupul de procese din foreground, deci
ajung la fiecare proces al job-ului din foreground. În coloana STAT a lui ps, + marchează procesele din
grupul din foreground.
[student@fedora ~]$ echo $$
3000
[student@fedora ~]$ sleep 100 | cat &
[1] 4243
[student@fedora ~]$ ps -o pid,ppid,pgid,sid,tty,stat,comm
PID PPID PGID SID TT STAT COMMAND
3000 2990 3000 3000 pts/0 Ss bash
4242 3000 4242 3000 pts/0 S sleep
4243 3000 4242 3000 pts/0 S cat
4250 3000 4250 3000 pts/0 R+ ps
basheste liderul sesiunii: PID-ul lui esteSID-ul tuturor proceselor din terminal și are unsînSTAT.sleepșicatformează un singur job, deci un singur grup:4242, PID-ul luisleep, primul proces al job-ului.psrulează în foreground (+), într-un grup separat.
Închiderea terminalului: SIGHUP și nohup
Când se închide terminalul, nucleul îi trimite SIGHUP (hang up) liderului sesiunii, adică shell-ului. Shell-ul
trimite SIGHUP tuturor job-urilor sale, iar acestea se încheie. O comandă pornită cu nohup ignoră SIGHUP,
așa că rulează în continuare:
[student@fedora ~]$ sleep 1000 &
[1] 4300
[student@fedora ~]$ nohup sleep 2000 > /dev/null &
[2] 4301
nohup: ignoring input
Închidem fereastra acestui terminal și căutăm cele două job-uri dintr-un alt terminal:
[student@fedora ~]$ pgrep -a sleep
4301 sleep 2000
[student@fedora ~]$ ps -o pid,ppid,sid,tty,comm -p 4301
PID PPID SID TT COMMAND
4301 1 3000 ? sleep
sleep 1000a primitSIGHUPși s-a încheiat.sleep 2000l-a ignorat și rulează în continuare, fără terminal (?); părintele lui s-a încheiat, așa că noul părinte este procesul cu PID-ul1.
Dacă nu-i redirectați ieșirea, nohup o scrie în fișierul nohup.out, pentru că terminalul nu va mai exista:
nohup: ignoring input and appending output to 'nohup.out'.
Dacă shell-ul este omorât cu SIGKILL, nu mai apucă să trimită SIGHUP job-urilor sale: acestea rulează în
continuare, fără terminal, chiar și fără nohup.
Variabile de mediu
Fiecare proces are un mediu (environment): o listă de variabile, fiecare scrisă ca NAME=value. Programele îl
citesc ca să se configureze: HOME le spune care este directorul utilizatorului, LANG care este limba, iar PATH
unde să caute programe. Numele țin cont de majuscule și minuscule (HOME și home sunt variabile diferite) și, prin
convenție, se scriu cu MAJUSCULE.
Un proces își primește mediul de la părinte când pornește: de obicei o copie a mediului părintelui. De acolo înainte, cele două sunt separate: o schimbare într-unul nu ajunge în celălalt.
| Comandă | Ce face |
|---|---|
printenv | Afișează toate variabilele de mediu |
printenv <NAME> | Afișează valoarea unei variabile |
env | Afișează toate variabilele de mediu, ca printenv |
echo $NAME | Shell-ul înlocuiește $NAME cu valoarea ei, apoi echo o afișează |
[student@fedora ~]$ printenv HOME
/home/student
[student@fedora ~]$ echo "I am $USER, my shell is $SHELL"
I am student, my shell is /bin/bash
[student@fedora ~]$ env | wc -l
35
Variabile de shell și export
Shell-ul are două feluri de variabile:
- variabile de shell: există doar în interiorul acestui shell;
- variabile de mediu: fac parte din mediu, deci fiecare comandă pornită din acest shell primește o copie a lor.
| Sintaxă | Ce face |
|---|---|
NAME=value | Creează o variabilă de shell sau schimbă valoarea unei variabile existente |
export NAME | Face din variabilă o variabilă de mediu: o primește fiecare comandă pornită de acum înainte |
export NAME=value | Ambele deodată |
NAME=value command | O variabilă de mediu doar pentru command; shell-ul n-o păstrează |
unset NAME | Șterge variabila, din shell și din mediu |
$NAME | Shell-ul îl înlocuiește cu valoarea variabilei sau cu nimic, dacă variabila nu există |
${NAME} | La fel, dar cu limite clare: "${NAME}_old" este valoarea urmată de _old; "$NAME_old" este variabila NAME_old |
[student@fedora ~]$ GREETING=hello
[student@fedora ~]$ echo $GREETING
hello
[student@fedora ~]$ bash -c 'echo "child: $GREETING"'
child:
[student@fedora ~]$ export GREETING
[student@fedora ~]$ bash -c 'echo "child: $GREETING"'
child: hello
[student@fedora ~]$ unset GREETING
[student@fedora ~]$ echo "[$GREETING]"
[]
GREETING=helloa creat o variabilă de shell, iar shell-ul a înlocuit$GREETINGcuhello.bash -c '...'pornește un shell nou, un copil, care rulează textul dintre ghilimele. Copilul nu a primitGREETING, pentru că variabila nu făcea parte din mediu.- După
export, copilul a primit o copie a variabilei. - După
unset, variabila nu mai există, iar$GREETINGa fost înlocuit cu nimic.
Fără spații în jurul lui =. GREETING = hello rulează comanda GREETING, cu parametrii = și hello:
bash: GREETING: command not found. Puneți valoarea între ghilimele când conține spații: GREETING="good morning".
Un proces își poate schimba doar propriul mediu și mediul copiilor pe care îi pornește după aceea. Un copil nu
poate schimba niciodată o variabilă a părintelui: de aceea export și unset sunt comenzi ale shell-ului, nu
programe.
O variabilă pentru o singură comandă
Scris înaintea unei comenzi, NAME=value pune variabila doar în mediul acelei comenzi:
[student@fedora ~]$ date +%H:%M
11:47
[student@fedora ~]$ TZ=Asia/Tokyo date +%H:%M
18:47
[student@fedora ~]$ echo "[$TZ]"
[]
[student@fedora ~]$ ls /nope
ls: cannot access '/nope': No such file or directory
[student@fedora ~]$ LANG=ro_RO.UTF-8 ls /nope
ls: nu se poate accesa '/nope': Fișier sau director inexistent
TZ(time zone, fusul orar) a schimbat fusul orar doar pentru aceldate; shell-ul nu l-a păstrat.LANGa schimbat limba mesajelor luils(dacă limba română este instalată).
Variabile definite de sistem
Când apare promptul, shell-ul are deja multe variabile, definite la autentificare sau de fișierele lui de pornire:
| Variabilă | Valoare |
|---|---|
HOME | directorul utilizatorului, ~ |
USER | numele de utilizator |
SHELL | shell-ul utilizatorului, din /etc/passwd |
PWD, OLDPWD | directorul curent și cel anterior (cd - merge în OLDPWD) |
PATH | directoarele cu programe, separate prin : (vedeți Căutarea în PATH) |
LANG | limba și codificarea caracterelor |
PS1 | promptul; o variabilă de shell, neexportată |
PS1 este textul promptului, cu câteva secvențe speciale: \u este utilizatorul, \h calculatorul, \w directorul
curent, iar \$ este $ (sau # pentru root). Încercați PS1='\u in \w \$ '. Schimbarea ține până închideți
terminalul.
Parametri speciali: $?, $$ și $!
Shell-ul mai definește câteva valori, pe care le puteți doar citi:
| Parametru | Valoare |
|---|---|
$? | codul de ieșire al ultimei comenzi: 0 înseamnă succes, orice altceva o eroare |
$$ | PID-ul shell-ului |
$! | PID-ul job-ului trimis cel mai recent în background, cu & sau bg |
[student@fedora ~]$ ls /nope
ls: cannot access '/nope': No such file or directory
[student@fedora ~]$ echo $?
2
[student@fedora ~]$ echo $?
0
ls a eșuat, cu codul de ieșire 2. Al doilea echo $? a afișat codul de ieșire al primului echo, care a reușit:
$? se schimbă după fiecare comandă.
Linia de comandă
Înainte să pornească o comandă, shell-ul citește toată linia și o împarte în părți:
[student@fedora lab04]$ LANG=C sort -r "$HOME/my notes.txt" my\ list.txt *.csv > sorted.txt 2> errors.txt
| Parte | În exemplu | Ce este |
|---|---|---|
| atribuiri | LANG=C | cuvinte NAME=value de la început: variabile doar pentru această comandă |
| comanda | sort | primul cuvânt care nu este o atribuire sau o redirectare |
| parametrii | -r, "$HOME/my notes.txt", my\ list.txt, *.csv | tot restul: opțiuni, nume de fișiere, texte |
| redirectări | > sorted.txt, 2> errors.txt | o redirectare și cuvântul de după ea; sunt pentru shell, nu pentru comandă, și pot apărea oriunde |
Apoi, pas cu pas, shell-ul:
- împarte linia în cuvinte la spații, cu excepția celor dintre ghilimele și a celor de după un
\; - pune deoparte atribuirile și redirectările;
- înlocuiește
$NAMEși~cu valorile lor; - împarte din nou valorile variabilelor care nu erau între ghilimele;
- expandează globurile (
*.csv) în numele fișierelor care se potrivesc; - elimină ghilimelele și
\; - pregătește redirectările și rulează comanda, cu cuvintele rămase drept parametri.
sort primește 5 parametri: -r, /home/student/my notes.txt, my list.txt, a.csv și b.csv. Nu vede niciodată
ghilimelele, $, * sau redirectările.
Ca să vedeți cuvintele pe care le primește o comandă, folosiți printf '[%s]\n' în locul comenzii: afișează fiecare
parametru între paranteze drepte, pe câte o linie.
[student@fedora lab04]$ printf '[%s]\n' -r "$HOME/my notes.txt" my\ list.txt *.csv
[-r]
[/home/student/my notes.txt]
[my list.txt]
[a.csv]
[b.csv]
Ordinea redirectărilor
Redirectările pot fi scrise oriunde în linie, chiar și înaintea comenzii, iar shell-ul le aplică de la stânga la dreapta:
[student@fedora lab04]$ 2> errors.txt ls > list.txt /nope .
[student@fedora lab04]$ ls /nope > all.txt 2>&1
[student@fedora lab04]$ ls /nope 2>&1 > all.txt
ls: cannot access '/nope': No such file or directory
- Prima linie este echivalentă cu
ls /nope . > list.txt 2> errors.txt. > all.txt 2>&1: întâi1merge înall.txt, apoi2merge unde merge1acum, deci tot înall.txt.2>&1 > all.txt: întâi2merge unde merge1în acel moment, adică pe ecran, apoi doar1se schimbă înall.txt. Eroarea a rămas pe ecran.
Ghilimele și caracterul \
Unele caractere au o semnificație specială pentru shell: spațiul, $, *, ?, [, {, ~, >, <, |, &,
;, #, ', " și \. Ghilimelele și \ le anulează semnificația specială:
| Scris ca | Ce face |
|---|---|
'...' | ghilimele simple: tot ce e înăuntru este text simplu, nu se înlocuiește nimic |
"..." | ghilimele duble: $NAME se înlocuiește în continuare, dar rezultatul rămâne un singur cuvânt; globurile nu se expandează |
\ | următorul caracter este text simplu: \ este un spațiu care nu desparte cuvinte, iar \$ este un $ |
Cu NAME="Alice Smith" (trei spații), iată ce primește echo:
| Tastați | echo primește | De ce |
|---|---|---|
echo $NAME | Alice, Smith | fără ghilimele: înlocuit, apoi împărțit la spații |
echo "$NAME" | Alice Smith | ghilimele duble: înlocuit, un singur cuvânt, spațiile se păstrează |
echo '$NAME' | $NAME | ghilimele simple: nu se înlocuiește nimic |
echo \$NAME | $NAME | \$: $ este doar un caracter |
echo my\ file.txt | my file.txt | \ : spațiul nu desparte cuvintele |
echo "\$NAME is $NAME" | $NAME is Alice Smith | în "...", \$ este un $, iar $NAME se înlocuiește |
echo "it's" 'say "hi"' | it's, say "hi" | fiecare fel de ghilimele îl protejează pe celălalt |
echo 'a\b' | a\b | în '...', \ este doar un caracter |
[student@fedora ~]$ NAME="Alice Smith"
[student@fedora ~]$ echo $NAME
Alice Smith
[student@fedora ~]$ echo "$NAME"
Alice Smith
[student@fedora ~]$ printf '[%s]\n' $NAME
[Alice]
[Smith]
Observăm că echo $NAME a primit două cuvinte și le-a afișat cu un singur spațiu între ele: cele trei spații s-au
pierdut.
Puneți fiecare variabilă între ghilimele duble, "$NAME", dacă nu vreți neapărat s-o împărțiți în cuvinte. Un nume
de fișier cu spațiu, ținut într-o variabilă, strică o comandă scrisă fără ghilimele: rm $FILE cu
FILE="my notes.txt" încearcă să șteargă my și notes.txt.
Globbing
Un glob este un tipar pentru nume de fișiere. Shell-ul îl înlocuiește cu numele fișierelor care se potrivesc, în ordine alfabetică, înainte să pornească comanda. Pentru exemple folosim acest director:
[student@fedora glob]$ ls
docs notes.txt photo1.png photo2.png photo3.png photoA.png report.md todo.txt
| Glob | Se potrivește cu | echo primește |
|---|---|---|
*.txt | *: oricâte caractere, sau niciunul (dar nu /) | notes.txt, todo.txt |
photo?.png | ?: exact un caracter | photo1.png, photo2.png, photo3.png, photoA.png |
photo[12].png | [12]: un caracter, 1 sau 2 | photo1.png, photo2.png |
photo[0-9].png | [0-9]: un caracter din interval | photo1.png, photo2.png, photo3.png |
photo[!0-9].png | [!0-9]: un caracter care nu este în interval (merge și [^0-9]) | photoA.png |
docs/**/*.txt | **: oricâte directoare, la orice adâncime (necesită shopt -s globstar) | docs/2026/exam.txt, docs/plan.txt |
{a,b,c}.pdf | {...}: câte un cuvânt pentru fiecare, chiar dacă fișierul nu există | a.pdf, b.pdf, c.pdf |
*.pdf | niciun fișier nu se potrivește | *.pdf, globul ca atare |
Când niciun fișier nu se potrivește, bash îi dă comenzii globul ca atare: ls *.pdf afișează
ls: cannot access '*.pdf': No such file or directory. Alte shell-uri, cum este zsh, afișează o eroare și nu mai
rulează deloc comanda.
*și?nu se potrivesc cu.de la începutul unui fișier ascuns:*nu include.bashrc, dar.*îl include.{...}nu este un glob: generează cuvintele chiar dacă fișierele nu există, ceea ce e util ca să le creați, de exemplumkdir -p 2026/{jan,feb,mar}sautouch photo{4..6}.png({4..6}înseamnă4 5 6).**funcționează doar dupăshopt -s globstar; fără această opțiune este același lucru cu*.- Între ghilimele, un glob este text simplu:
echo "*.txt"afișează*.txt. Așa îi dați un tipar unei comenzi care știe singură să-l interpreteze, cum estefind . -name "*.txt".
Rularea unei comenzi
Comenzi interne și externe
Nu orice comandă este un program:
| Tip | Ce este | Exemple | Ajutor |
|---|---|---|---|
| comandă internă (built-in) | parte din shell: rulează în interiorul lui, fără un proces nou | cd, export, unset, exit, jobs, fg, bg, trap, type | help <name> |
| unele sunt interne doar ca să fie mai rapide și există și ca programe | echo, printf, kill, pwd | ||
| comandă externă | un fișier executabil de pe disc, găsit în PATH; shell-ul îl pornește ca proces copil | ls, sort, grep, sleep, bash | man <name> |
type vă spune ce fel de comandă este:
[student@fedora ~]$ type cd
cd is a shell builtin
[student@fedora ~]$ type sort
sort is /usr/bin/sort
[student@fedora ~]$ type ls
ls is aliased to `ls --color=auto'
[student@fedora ~]$ type -a echo
echo is a shell builtin
echo is /usr/bin/echo
cdeste o comandă internă, iarsorteste fișierul/usr/bin/sort.lseste un alias: un nume scurt pentru o altă linie de comandă, aicils --color=auto.-aa afișat toate semnificațiile numelui:echoeste o comandă internă și există și un program cu același nume. Shell-ul o folosește pe prima.
cd, export, unset și exit trebuie să fie interne: schimbă shell-ul însuși (directorul lui curent,
variabilele lui) sau îl opresc. Un program pornit de shell este un proces copil, iar un copil nu își poate schimba
părintele.
Căutarea în PATH
Nucleul poate porni un program doar pornind de la calea lui. Când primul cuvânt este un nume, shell-ul trebuie să găsească fișierul:
- dacă cuvântul conține un
/, este o cale: absolută (/usr/bin/sort) sau relativă la directorul curent (./hello,bin/hello,../tools/hello), folosită ca atare; - altfel, dacă este un alias sau o comandă internă, shell-ul o rulează singur;
- altfel, shell-ul caută prin directoarele din variabila
PATH, de la stânga la dreapta, și rulează primul fișier cu acel nume; - dacă niciun director nu îl conține:
command not found.
[student@fedora ~]$ echo $PATH
/home/student/.local/bin:/home/student/bin:/usr/local/bin:/usr/bin
[student@fedora ~]$ cd ~/lab04
[student@fedora lab04]$ ls bin
hello
[student@fedora lab04]$ hello
bash: hello: command not found
[student@fedora lab04]$ bin/hello
Hello!
[student@fedora lab04]$ PATH="$PATH:$HOME/lab04/bin"
[student@fedora lab04]$ hello
Hello!
[student@fedora lab04]$ type hello
hello is /home/student/lab04/bin/hello
- Shell-ul nu caută în directorul curent, așa că
hellosimplu nu a fost găsit. bin/helloconține un/, deci este o cale, iar shell-ul l-a rulat.- După ce am adăugat
~/lab04/binla finalul luiPATH, shell-ul a găsithelloacolo.
Adăugați directoare în PATH, nu-l înlocuiți niciodată: PATH=~/lab04/bin singur lasă shell-ul fără /usr/bin, iar
ls, cat și aproape toate celelalte comenzi devin command not found. Închideți terminalul și deschideți altul ca
să recăpătați vechiul PATH.
Pe Fedora, ~/.local/bin și ~/bin sunt deja în PATH: copiați acolo un program de-al vostru și va merge din orice
director, în orice terminal nou.
Depanare
| Problemă | Soluție |
|---|---|
| Ctrl+C nu oprește un program | Programul tratează sau ignoră SIGINT: încercați Ctrl+\ sau Ctrl+Z urmat de kill %1; în ultimă instanță, kill -KILL <PID> din alt terminal |
kill nu încheie un proces | Procesul tratează SIGTERM: folosiți kill -KILL <PID> |
Un proces oprit nu reacționează la kill | Primește semnalul abia când își continuă execuția: kill -CONT <PID> sau folosiți kill -KILL <PID> |
bash: kill: (4242) - Operation not permitted | Procesul aparține altui utilizator |
bash: kill: %1: no such job sau fg: current: no such job | Job-ul aparține altui terminal (fiecare shell are job-urile lui) sau s-a încheiat deja; folosiți PID-ul |
[1]+ Stopped imediat după command & | Job-ul încearcă să citească tastatura: aduceți-l în foreground cu fg sau redirectați-i intrarea |
There are stopped jobs. când închid shell-ul | Continuați-le sau încheiați-le (fg, kill %N); un al doilea exit le încheie |
| Un job scrie peste comenzile pe care le tastez | Redirectați-i ieșirea într-un fișier sau în /dev/null |
bash: NAME: command not found după NAME = value | Fără spații în jurul lui = |
| Un proces copil nu vede o variabilă | Este o variabilă de shell: exportați-o cu export |
bash: <command>: command not found | Verificați numele, apoi echo $PATH: directorul programului nu este în PATH sau PATH a fost înlocuit; deschideți un terminal nou |
Un fișier din directorul curent dă command not found | Shell-ul nu caută în directorul curent: rulați-l ca ./name |
cannot access '*.pdf' | Niciun fișier nu se potrivește cu globul, așa că comanda l-a primit ca atare |
| Un nume cu spațiu devine două fișiere | Puneți-l între ghilimele duble, sau variabila care îl conține |
Shell-ul afișează > și așteaptă | Ați lăsat ghilimelele deschise: închideți-le sau apăsați Ctrl+C și tastați din nou linia |
Exerciții
Exercițiile devin tot mai grele pe parcurs:
- 🧪 Primii pași (exercițiile 1 - 23) trece o dată prin tot ce conține laboratorul, cu exerciții ușoare;
- 🏠 Mai departe (exercițiile 24 - 51) are mai multe exerciții de făcut acasă, tot mai grele;
- 📝 Provocări (exercițiile 52 - 62) le conține pe cele mai grele.
Exercițiul 1 creează fișierele și directoarele de la care pornesc toate celelalte exerciții. Rulați comenzile în
~/lab04, dacă exercițiul nu spune altfel. Dacă modificați sau ștergeți unul dintre fișierele din exercițiul 1,
rulați din nou comenzile lui ca să-l recăpătați.
Unele exerciții vă cer lucruri pe care laboratorul nu vi le arată, de exemplu o opțiune care nu apare în tabelele de
mai sus. Găsiți-o în pagina de manual a comenzii (man <command>), cum am explicat în
Căutarea într-o pagină de manual din laboratorul 02. jobs, fg, bg,
trap, export și celelalte comenzi interne nu au o pagină de manual proprie: folosiți help <command>, de exemplu
help trap, sau căutați în man bash.
Țineți două terminale unul lângă altul în Sway: primul pentru a rula comenzile, iar al doilea pentru a rula
comenzile pe care le inspectați sau pentru a verifica rezultatele. Multe exerciții folosesc PID-ul unui proces: pe
calculatorul vostru este altul, deci folosiți-l pe cel afișat de pgrep, ps sau $!.
Nu folosiți sudo ca să trimiteți semnale: sudo kill poate încheia procesele sistemului. Trimiteți semnale doar
proceselor voastre și verificați de două ori PID-ul înainte de kill -KILL.
🧪 Primii pași
-
Arborele de exerciții:
-
rulați comenzile de mai jos ca să creați fișierele folosite în exerciții (copiați-le din browser cu Ctrl+C și lipiți-le în terminal cu Ctrl+Shift+V):
mkdir -p ~/lab04/bin ~/lab04/glob/docs/2026cd ~/lab04printf 'pear\nfig\n' > 'my notes.txt'printf 'cherry\nBanana\napple\n' > 'my list.txt'printf 'banana,3\napple,5\n' > a.csvprintf 'cherry,1\nkiwi,7\n' > b.csvprintf '#!/bin/bash\necho Hello!\n' > bin/hellochmod +x bin/hellocat > stubborn.sh << 'EOF'#!/bin/bash# the signal handlerfunction no_interruptions_please () {echo "No interruptions please"}# register the signal handlertrap no_interruptions_please SIGINTwhile true; doecho "working"sleep 1doneEOFcd ~/lab04/globtouch notes.txt todo.txt report.md photo1.png photo2.png photo3.png photoA.png docs/plan.txt docs/2026/exam.txtcd ~/lab04 -
rulați
treeîn~/lab04; -
afișați
stubborn.shcucat.
Verificare: vedeți
bin/hello, cele două fișiere.csv,my list.txt,my notes.txt,stubborn.shși directorulglobcu 9 fișiere;stubborn.sheste scriptul din Tratarea unui semnal.chmod +xface dinhelloun program care poate fi rulat (veți afla mai multe în laboratorul despre permisiuni). → Navigare -
Terminale
-
Ce terminal:
- afișați terminalul shell-ului, în ambele terminale;
- listați pseudoterminalele din
/dev/pts; - deschideți un al treilea terminal și listați-le din nou, apoi închideți-l.
Verificare: fiecare shell are propriul
/dev/pts/<n>; al treilea terminal adaugă încă un număr în listă, iar numărul lui dispare după ce îl închideți. → Emulatoare de terminal -
Scrieți în alt terminal:
- aflați terminalul celui de-al doilea shell, cu
tty; - din primul terminal, scrieți
hello from the first terminalîn al doilea, cuechoși o redirectare.
Verificare: textul apare în al doilea terminal, fără să fi tastat nimic acolo. → Emulatoare de terminal
- aflați terminalul celui de-al doilea shell, cu
-
Un terminal virtual:
- într-un terminal din Sway, aflați cu
loginctlîn ce terminal virtual rulează Sway; - comutați pe al treilea terminal virtual cu Ctrl+Alt+F3 (Host+F3 în VirtualBox);
- autentificați-vă ca
student, afișați numele terminalului și listați-i procesele cups; - ieșiți din sesiune cu
exit; - reveniți în Sway, folosind numărul afișat de
loginctl.
Verificare:
VTNreste1sau2;ttyafișează/dev/tty3, iarpsarată unbashpetty3; dupăexit, terminalul cere din nou autentificarea; înapoi în Sway, ferestrele sunt exact cum le-ați lăsat. → Terminale virtuale - într-un terminal din Sway, aflați cu
Semnale
-
Lista semnalelor:
- listați toate semnalele cu
kill; - afișați cu
kill -lnumele semnalului9și numărul luiSIGCONT.
Verificare: semnalul
9esteKILL, iarCONTeste18. → Trimiterea unui semnal - listați toate semnalele cu
-
Stop și continuare:
- în al doilea terminal, rulați
sleep 1000; - în primul terminal, aflați-i PID-ul și opriți-l cu un semnal;
- afișați-i starea cu
ps -o pid,stat,cmd -p <PID>și uitați-vă la al doilea terminal; - faceți-l să continue, afișați-i din nou starea și rulați
jobsîn al doilea terminal; - încheiați-l cu
kill %1în al doilea terminal.
Verificare: starea este
TdupăSIGSTOPșiSdupăSIGCONT, fără+. Imediat cesleeps-a oprit, al doilea shell a afișat[1]+ Stopped (signal), a preluat înapoi terminalul și a afișat promptul, așa că dupăSIGCONT,jobsaratăsleepcaRunningîn background. → Trimiterea unui semnal - în al doilea terminal, rulați
-
Taste și semnale:
- rulați
sleep 100și încheiați-l cu Ctrl+C; - rulați-l din nou și încheiați-l cu Ctrl+\;
- rulați-l din nou și opriți-l cu Ctrl+Z;
- listați job-urile.
Verificare: terminalul afișează
^Cdupă Ctrl+C,Quit (core dumped)după Ctrl+\ (doarQuitdacă fișierele core dump sunt dezactivate) șiStoppeddupă Ctrl+Z;sleepapare încă în lista de job-uri. Încheiați-l cukill %1. → Taste care trimit semnale - rulați
-
Un script încăpățânat:
- rulați
bash stubborn.sh; - apăsați de câteva ori Ctrl+C;
- opriți-l cu Ctrl+\.
Verificare: Ctrl+C afișează
No interruptions please, iar scriptul lucrează mai departe; Ctrl+\ îl încheie. → Tratarea unui semnal - rulați
Foreground și background
-
Două job-uri:
- porniți
sleep 300șisleep 400în background; - listați job-urile, apoi job-urile împreună cu PID-urile lor;
- afișați cu
$!PID-ul ultimului job; - încheiați primul job folosind ID-ul lui de job;
- încheiați și al doilea job și listați din nou job-urile.
Verificare:
jobsarată[1]și[2], cu+la[2];$!este PID-ul luisleep 400; după următoarea comandă, shell-ul afișează[1]- Terminated; la final,jobsnu afișează nimic. → Pornirea unui job - porniți
-
În background și înapoi:
- rulați
sleep 200 > /dev/nullîn foreground; - opriți-l de la tastatură;
- faceți-l să continue în background și verificați cu
jobscă rulează; - aduceți-l înapoi în foreground și încheiați-l de la tastatură.
Verificare:
jobsaratăStopped, apoiRunning; dupăfg, promptul nu apare până nu apăsați Ctrl+C. → Oprire și continuare - rulați
-
Job-ul curent:
- porniți
sleep 300,sleep 400șisleep 500în background; - listați job-urile și găsiți-l pe cel curent;
- rulați
fgfără ID de job și încheiați job-ul cu Ctrl+C; - listați din nou job-urile și încheiați-le pe celelalte cu
kill.
Verificare: job-ul curent (
+) estesleep 500, ultimul pornit, iarfgl-a adus în foreground; după ce s-a încheiat,sleep 400a devenit job-ul curent. → Pornirea unui job - porniți
Sesiuni
-
Sesiune și grupuri:
- afișați PID-ul shell-ului;
- porniți
sleep 100 | catîn background; - listați procesele terminalului cu coloanele
pid,ppid,pgid,sid,tty,statșicomm.
Verificare: toate procesele au ca
SIDPID-ul shell-ului;sleepșicatau acelașiPGID, PID-ul luisleep;bashares, iarpsare+înSTAT. → Sesiuni și grupuri de procese -
Închideți terminalul:
- deschideți un terminal nou și porniți în el
sleep 1000 &șinohup sleep 2000 > /dev/null &; - închideți fereastra acestui terminal;
- într-un alt terminal, căutați cele două procese
sleep; - încheiați
sleep 2000cukill.
Verificare: doar
sleep 2000mai rulează, fără terminal (?). Părintele lui s-a încheiat, așa că a fost adoptat: noul părinte este procesul cu PID-ul1sau un proces care adoptă orfanii, cum estesystemd --user(vedeți care cups -o pid,comm -p <PPID>). → Închiderea terminalului - deschideți un terminal nou și porniți în el
Variabile
-
Citiți variabile:
- afișați
HOME,USERșiSHELLcuprintenv; - afișați propoziția
I am student and I live in /home/studentcuechoși variabileleUSERșiHOME; - numărați variabilele de mediu.
Verificare: propoziția conține utilizatorul și directorul vostru; există câteva zeci de variabile. → Variabile de mediu
- afișați
-
Variabilă de shell sau variabilă de mediu:
- dați variabilei de shell
COLORvaloareablueși afișați-o; - verificați cu
printenv COLORși cubash -c 'echo "$COLOR"'dacă este în mediu; - exportați-o și verificați din nou;
- ștergeți-o și afișați-o din nou.
Verificare: înainte de
export,printenvnu afișează nimic, iar copilul afișează o linie goală; dupăexport, ambele afișeazăblue; dupăunset,echoafișează o linie goală. → Variabile de shell șiexport - dați variabilei de shell
-
Doar pentru o comandă:
- afișați ora cu
date +%H:%M; - afișați ora din Tokyo și din New York (
America/New_York), definindTZdoar pentrudate; - afișați
TZ.
Verificare: cele trei ore sunt diferite, iar
TZeste gol în shell. → O variabilă pentru o singură comandă - afișați ora cu
-
Coduri de ieșire:
- rulați
ls ~/lab04și afișați-i codul de ieșire; - rulați
ls /nopeși afișați-i codul de ieșire; - rulați
grep apple a.csvșigrep pear a.csvși afișați codul de ieșire după fiecare; - rulați de două ori la rând
echo $?.
Verificare:
lsare codul de ieșire0, apoi2;grepare0când găsește textul și1când nu îl găsește; al doileaecho $?afișează întotdeauna0. → Parametri speciali - rulați
Linia de comandă
-
Cuvintele unei comenzi: Folosiți
printf '[%s]\n'ca să vedeți ce cuvinte îi dă shell-ul unei comenzi:printf '[%s]\n' one two three;printf '[%s]\n' "one two three";printf '[%s]\n' ~ "~" '$HOME' "$HOME".
Verificare: prima linie dă 3 cuvinte, a doua doar unul, cu spațiile păstrate;
~și"$HOME"devin/home/student, iar"~"și'$HOME'rămân neschimbate. → Linia de comandă · Ghilimele și caracterul\ -
Ghilimele și variabile:
- definiți
NAME="Alice Smith"; - afișați-o cu
echo: fără ghilimele, între ghilimele duble și între ghilimele simple; - afișați textul
$NAME is Alice Smithcu un singurechoși o singură pereche de ghilimele duble.
Verificare: fără ghilimele, cele trei spații devin unul; între ghilimele duble se păstrează; între ghilimele simple,
echoafișează$NAME; ultimulechoafișează$NAME is Alice Smith, cu cele trei spații. → Ghilimele și caracterul\ - definiți
-
Semnul dolarului: Afișați textul
The price is $5cuecho:- întâi doar cu ghilimele duble și observați ce nu merge;
- cu ghilimele simple;
- cu un
\și fără ghilimele; - cu ghilimele duble și un
\.
Verificare: pasul 1 afișează
The price is: shell-ul a înlocuit$5cu nimic.$5este al cincilea parametru pozițional, un parametru primit de un script, și este gol în shell-ul vostru. Ghilimelele simple și\anulează semnificația lui$, așa că pașii 2, 3 și 4 afișeazăThe price is $5. → Ghilimele și caracterul\ -
Globuri: Mergeți în
~/lab04/globși afișați cuecho:- toate fișierele
.png; - pozele cu o singură cifră după
photo; - pozele al căror nume nu are o cifră după
photo; - toate fișierele al căror nume are exact 8 caractere (numărați și extensia);
- toate fișierele
.pdf; - numele
photo7.pngșiphoto8.png, cu{...}.
Verificare:
photoA.pngapare în prima și în a treia listă, nu și în a doua; a patra listă estetodo.txt; al cincileaechoafișează*.pdf, pentru că niciun fișier nu se potrivește; ultimul afișează ambele nume, chiar dacă fișierele nu există. → Globbing - toate fișierele
-
Internă sau externă:
- aflați cu
typece suntcd,ls,sort,typeșisleep; - afișați toate semnificațiile lui
killși ale luiecho; - afișați
PATHși găsiți în care dintre directoarele lui se aflăsort.
Verificare:
cdșitypesunt interne,lseste un alias,sortșisleepsunt în/usr/bin;killșiechosunt interne și există și ca programe în/usr/bin. Pentru un program pe care l-ați rulat deja,typespunehashed, de exemplusleep is hashed (/usr/bin/sleep): shell-ul ține minte unde l-a găsit. → Comenzi interne și externe · Căutarea înPATH - aflați cu
-
Comandă negăsită:
- rulați
sl(o greșeală de tastare pentruls) și afișați codul de ieșire; - aflați cu
typedacăslexistă; - rulați
/usr/bin/nopeși afișați codul de ieșire.
Verificare: shell-ul a căutat prin toate directoarele din
PATHși nu a găsit niciunsl: codul de ieșire este127, codul shell-ului pentru command not found;/usr/bin/nopeeste o cale care nu există, iar codul de ieșire este tot127. Dacă shell-ul se oferă să instaleze un pachet pentrusl, răspundețiN. → Căutarea înPATH· Parametri speciali - rulați
🏠 Mai departe
-
Terminalul în ps:
- listați cu
psprocesele terminalului; - afișați din
/proc/$$/fddescriptorii de fișier0,1și2ai shell-ului.
Verificare: coloana
TTYdinpsși cele trei link-uri din/proc/$$/fdarată același pseudoterminal catty. → Emulatoare de terminal · Sistemul de fișiere/proc - listați cu
-
Nume și numere:
- în al doilea terminal, rulați
sleep 1000; - în primul terminal, încheiați-l cu
kill, dând semnalulSIGTERMprin numărul lui; - rulați din nou
sleep 1000și încheiați-l, dândSIGTERMprin numele lui, cuSIGîn față.
Verificare: de ambele dăți, al doilea terminal afișează
Terminated:kill -15,kill -TERMșikill -SIGTERMtrimit același semnal. → Trimiterea unui semnal - în al doilea terminal, rulați
-
Încheiați un proces:
- în al doilea terminal, rulați
sleep 1000; - în primul terminal, încheiați-l cu
killși semnalul lui implicit; - rulați din nou
sleep 1000și încheiați-l cuSIGKILL.
Verificare: al doilea terminal afișează
Terminatedprima dată șiKilleda doua oară. → Trimiterea unui semnal - în al doilea terminal, rulați
-
SIGINT trimis cu kill:
- în al doilea terminal, rulați
bash stubborn.sh; - în primul terminal, aflați PID-ul scriptului (căutați
stubborncupgrep -f); - trimiteți-i
SIGINTcukill; - încheiați-l cu
SIGTERM.
Verificare:
SIGINTtrimis cukillare același efect ca Ctrl+C: mesajul apare în al doilea terminal;SIGTERMîncheie scriptul, pentru că nu are o rutină de tratare pentru el. → Trimiterea unui semnal · Tratarea unui semnal - în al doilea terminal, rulați
-
O pauză de la editor:
- deschideți
stubborn.shcunano; - opriți
nanocu Ctrl+Z (dacă nu se oprește, apăsați Ctrl+T, apoi Ctrl+Z) și rulațils; - întoarceți-vă în
nanoși ieșiți din el; - faceți la fel cu
less stubborn.sh.
Verificare:
nanoșilessrevin exact cum le-ați lăsat. → Oprire și continuare - deschideți
-
Un job care citește:
- porniți
catîn background; - listați job-urile;
- aduceți-l în foreground, tastați două linii și încheiați-i intrarea.
Verificare:
catesteStoppedimediat; în foreground afișează înapoi fiecare linie pe care o tastați, iar Ctrl+D îl încheie. → Job-uri care citesc tastatura - porniți
-
Un job cu ieșirea lui:
- căutați cu
find, în background, fișierele numitepasswddin tot sistemul de fișiere, cu rezultatul înfound.txtși erorile aruncate; - rulați
jobspână când job-ul esteDone; - afișați
found.txt.
Verificare: nu apare nimic de la
findîn terminal, iarfound.txtconține/etc/passwd. → Pornirea unui job · Fișiere speciale - căutați cu
-
Job-urile aparțin unui shell:
- în al doilea terminal, porniți
sleep 600în background; - în primul terminal, listați job-urile;
- în primul terminal, aflați PID-ul lui
sleep 600cupgrep -f 'sleep 600'și încheiați-l.
Verificare: în primul terminal,
jobsnu afișează nimic: fiecare shell are doar job-urile lui.kill %1nu merge acolo, darkill <PID>merge, iar al doilea terminal afișeazăTerminateddupă următoarea lui comandă. → Pornirea unui job - în al doilea terminal, porniți
-
O tastă, tot grupul:
- rulați
sleep 100 | sleep 200în al doilea terminal; - în primul terminal, listați cele două procese cu
pgrep -a sleep; - apăsați Ctrl+C în al doilea terminal și listați-le din nou.
Verificare: ambele procese
sleeps-au încheiat în același timp: semnalul a ajuns la tot grupul din foreground. → Sesiuni și grupuri de procese - rulați
-
Spații în jurul lui
=:- rulați
GREETING = hello; - rulați
GREETING=good morning; - dați corect variabilei
GREETINGvaloareagood morningși afișați-o.
Verificare: prima comandă dă
command not foundpentruGREETING, a doua pentrumorning, pentru că shell-ul a rulatmorningcuGREETING=goodîn mediul lui. → Variabile de shell șiexport - rulați
-
Directorul anterior:
- mergeți în
/etc, apoi în~/lab04; - afișați
PWDșiOLDPWD; - rulați
cd -și afișați-le din nou.
Verificare:
cd -vă duce înapoi în/etc, iar cele două variabile își schimbă valorile între ele. → Variabile definite de sistem - mergeți în
-
Moștenite de copii:
- exportați variabila
LEVELcu valoarea1; - porniți un
bashnou (tastați doarbash), afișațiLEVELși dați-i valoarea2; - ieșiți din copil cu
exitși afișați din nouLEVEL.
Verificare: copilul a afișat
1, primit de la părinte; dupăexit, părintele are tot1: copilul și-a schimbat doar propria copie. → Variabile de shell șiexport - exportați variabila
-
PID-ul shell-ului:
- afișați PID-ul shell-ului cu
$$și comparați-l cu linia luibashafișată deps; - porniți un
bashnou (tastațibash) și afișați din nou$$șips; - ieșiți din el cu
exitși afișați încă o dată$$.
Verificare: în noul
bash,$$este un alt număr, iarpsarată două procesebash; dupăexit,$$este din nou primul număr. → Parametri speciali - afișați PID-ul shell-ului cu
-
Mediul din /proc:
- în al doilea terminal, rulați
MESSAGE=secret sleep 1000; - în primul terminal, aflați PID-ul lui
sleepși afișați-i mediul din/proc/<PID>/environ, cu octeții\0înlocuiți cu linii noi (căutați înman trcum se înlocuiește un caracter), păstrând doar linia cuMESSAGE; - căutați la fel
MESSAGEîn mediul celui de-al doilea shell.
Verificare:
sleepareMESSAGE=secret, shell-ul din al doilea terminal nu./proc/<PID>/environarată mediul pe care l-a primit un proces când a pornit: o variabilă pe care shell-ul o exportă mai târziu nu apare acolo. → O variabilă pentru o singură comandă · Sistemul de fișiere/proc - în al doilea terminal, rulați
-
Limba mesajelor:
- rulați
ls /nopecuLANGsetat laro_RO.UTF-8doar pentruls; - sortați
my list.txtcusort(numele conține un spațiu), apoi cuLANG=Cdoar pentrusort.
Verificare: eroarea este în română (dacă limba este instalată);
sortafișeazăapple,Banana,cherry, dar cuLANG=Cafișează întâiBanana: compară octeții, iar toate majusculele vin înaintea minusculelor. → O variabilă pentru o singură comandă - rulați
-
Un prompt nou:
- setați
PS1astfel încât promptul să arate utilizatorul, calea directorului curent (cu~pentru directorul utilizatorului) și$(vedeți sfatul din Variabile definite de sistem); - mergeți în
~/lab04/glob; - verificați dacă
PS1este o variabilă de mediu.
Verificare: promptul se schimbă imediat și arată
~/lab04/glob;printenv PS1nu afișează nimic: este o variabilă de shell. Închideți terminalul ca să recăpătați vechiul prompt. → Variabile definite de sistem - setați
-
Desfaceți o linie de comandă: Fără s-o rulați, scrieți ce cuvinte primește
sortși unde merg rezultatul și erorile lui, pentru:LANG=C sort -r "$HOME/my notes.txt" my\ list.txt *.csv > sorted.txt 2> errors.txtapoi înlocuiți
sortcuprintf '[%s]\n'și eliminați redirectarea> sorted.txt, ca să vă verificați răspunsul.Verificare: 5 cuvinte:
-r,/home/student/my notes.txt,my list.txt,a.csvșib.csv. Ieșirea luisortmerge însorted.txt, iar erorile înerrors.txt;LANG=Cexistă doar în mediul lui. → Linia de comandă -
Redirectări oriunde:
- rulați
2> errors.txt ls > list.txt /nope .; - afișați cele două fișiere;
- scrieți aceeași comandă cu redirectările la final.
Verificare: de ambele dăți,
list.txtconține fișierele din~/lab04, iarerrors.txteroarea pentru/nope. → Ordinea redirectărilor - rulați
-
Ordinea redirectărilor:
- rulați
ls /nope . > all.txt 2>&1și afișațiall.txt; - rulați
ls /nope . 2>&1 > all.txtși afișațiall.txt.
Verificare: prima dată, atât lista, cât și eroarea sunt în fișier; a doua oară, eroarea apare pe ecran, pentru că
2a fost copiat din1înainte ca1să se schimbe. → Ordinea redirectărilor - rulați
-
Variabile cu spații:
- definiți
FILE="my notes.txt"; - rulați
cat $FILE, apoicat "$FILE"; - definiți
OPTS="-t, -k2 -n"și sortația.csvșib.csvcusort $OPTS, apoi cusort "$OPTS".
Verificare:
cat $FILEcautămyșinotes.txt;cat "$FILE"merge.sort $OPTSprimește trei opțiuni și sortează liniile după numărul de după virgulă:cherry,1,banana,3,apple,5,kiwi,7.sort "$OPTS"primește un singur cuvânt, așa că-tia, -k2 -ndrept separator:sort: multi-character tab. → Ghilimele și caracterul\ - definiți
-
Nume speciale:
- creați fișierele
rock & roll.txt,it's mine.txtși$HOME.txtcu o singură comandătouch; - listați-le cu
ls, apoi culs -N; - ștergeți-le cu o singură comandă
rm, de data aceasta folosind\în loc de ghilimele.
Verificare:
lsarată numele între ghilimele, doar la afișare;ls -Narată exact aceste trei nume, iar$HOME.txtnu este/home/student.txt.rmle șterge pe toate trei. → Ghilimele și caracterul\ - creați fișierele
-
Nume din acolade:
- în
~/lab04, creați directoareletrips/2025șitrips/2026cu o singură comandămkdir -pși{...}; - creați fișierele
jan.txt,feb.txtșimar.txtîn ambele directoare, cu o singură comandătouch; - afișați rezultatul cu
tree trips.
Verificare:
treearată două directoare cu câte trei fișiere; verificați ce cuvinte a generat shell-ul cuecho trips/{2025,2026}/{jan,feb,mar}.txt. → Globbing - în
-
Mai multe globuri: În
~/lab04/glob:- afișați toate fișierele
.txtdinglob, la orice adâncime (activați întâiglobstar); - afișați doar
photo1.pngșiphoto2.png, cu un singur glob; - creați
photo4.png,photo5.pngșiphoto6.pngcu un singurtouchși un interval{...}; - afișați fișierele ascunse din directorul utilizatorului cu
echo ~/.*și verificați dacă lista conține.și..; găsiți înman bashopțiuneashoptcare decide acest lucru.
Verificare: prima listă conține
docs/2026/exam.txt,docs/plan.txt,notes.txtșitodo.txt;photo?.pngse potrivește acum cu 7 fișiere;~/.*conține.bashrcși celelalte fișiere ascunse, dar nu și.și..: opțiuneaglobskipdotseste activată implicit începând cubash5.2. → Globbing - afișați toate fișierele
-
Glob sau text: În
~/lab04/glob:- rulați
find . -name *.txt; - rulați
find . -name "*.txt"; - aflați de ce eșuează prima, folosind
printf '[%s]\n'în loc defind.
Verificare: fără ghilimele, shell-ul a înlocuit
*.txtcunotes.txt todo.txtînainte să porneascăfind, așa căfinda primit parametri greșiți (find: paths must precede expression); cu ghilimele,finda primit tiparul și a găsit toate cele 4 fișiere. → Globbing - rulați
-
Rulați un program dintr-un director: În
~/lab04:- rulați
hello; - rulați-l cu o cale relativă, apoi cu o cale absolută;
- mergeți în
~/lab04/binși rulați-l de acolo; - adăugați
~/lab04/binla finalul luiPATH, mergeți în directorul utilizatorului și rulațihello.
Verificare:
hellosimplu dăcommand not found, darbin/hello,/home/student/lab04/bin/helloși, înbin,./hellomerg; după schimbarea luiPATH,hellomerge din orice director, iartype helloîi arată calea (hello is hashed (...), pentru că l-ați rulat deja). → Căutarea înPATH - rulați
-
Un PATH stricat: Deschideți un terminal nou:
- setați
PATHla/tmpși rulațils; - rulați
/usr/bin/ls; - rulați
cd ~șiecho $HOME; - închideți terminalul.
Verificare:
lsdăcommand not found, dar/usr/bin/lsmerge, pentru că este o cale;cdșiechomerg în continuare, pentru că sunt comenzi interne. → Căutarea înPATH - setați
-
Propriul bin:
- creați
~/.local/bin, dacă nu există, și copiați acolobin/hello; - deschideți un terminal nou și rulați
hellodin directorul utilizatorului; - aflați de unde a fost pornit.
Verificare:
type helloafișeazăhello is hashed (/home/student/.local/bin/hello): pe Fedora, acest director este deja înPATH. → Căutarea înPATH - creați
-
Cine câștigă:
- creați directorul
~/lab04/firstcu două programe,lsșiecho, care afișeazăNot the real ls!șiNot the real echo!(procedați ca pentrubin/hellodin exercițiul 1:printfcu ghilimele simple, apoichmod +x); - adăugați
~/lab04/firstla începutul luiPATH; - rulați
ls,\ls,/usr/bin/lsșiecho hi; - rulați
type -a lsșitype -a echo; - închideți terminalul.
Verificare:
lsși\ls(care ocolește aliasul) afișează amândouăNot the real ls!: aliasul doar înlocuiește cuvântullsculs --color=auto, iar acestlseste căutat tot înPATH, unde~/lab04/firstvine primul./usr/bin/lseste o cale, deci nu se mai caută nimic.echo hiafișeazăhi: o comandă internă are prioritate față de căutarea înPATH.type -alistează semnificațiile în ordinea în care le încearcă shell-ul. → Căutarea înPATH - creați directorul
📝 Provocări
-
Ignorați și restaurați:
- modificați
stubborn.shastfel încât să ignore șiSIGTERM; - rulați-l în al doilea terminal și încercați să-l încheiați din primul terminal cu
kill, apoi cukill -KILL; - în shell-ul vostru, faceți ca
SIGINTsă afișezeNot now, apăsați Ctrl+C la prompt, apoi reveniți la acțiunea implicită.
Verificare:
killnu are niciun efect asupra scriptului, iarkill -KILLîl încheie;trap -parată rutina de tratare a shell-ului înainte s-o eliminați. → Tratarea unui semnal - modificați
-
Omorâți shell-ul:
- deschideți un terminal nou și porniți în el
sleep 1000 &șisleep 2000 &; - afișați PID-ul acestui shell;
- dintr-un alt terminal, încheiați acest shell cu
SIGKILL; - listați procesele din sesiunea shell-ului omorât (căutați în
man psopțiunea care selectează o sesiune), cu coloanelepid,ppid,sid,ttyșicomm; - încheiați cele două procese
sleep.
Verificare: fereastra s-a închis, dar ambele procese
sleeprulează în continuare, fără terminal; noul lor părinte este procesul cu PID-ul1sau un proces care adoptă orfanii, cum estesystemd --user. Shell-ul nu a putut să le trimităSIGHUP. → Închiderea terminalului - deschideți un terminal nou și porniți în el
-
O sesiune în mod text:
- autentificați-vă pe
/dev/tty3(Ctrl+Alt+F3) și pornițisleep 1000 &; - reveniți în Sway și listați procesele acelui terminal cu
ps -t tty3 -o pid,ppid,sid,stat,comm; - găsiți liderul sesiunii de pe
tty3și părintele lui; - întoarceți-vă pe
tty3, încheiațisleepși ieșiți din sesiune.
Verificare:
basheste liderul sesiunii: aresînSTAT, iar PID-ul lui esteSID-ul luisleep; părintele lui estelogin, care v-a verificat parola și l-a pornit. → Terminale virtuale · Sesiuni și grupuri de procese - autentificați-vă pe
-
De ce cd este intern: Fedora are și un program numit
cd, în/usr/bin.- găsiți-l cu
type -a cd; - mergeți în directorul utilizatorului, rulați
/usr/bin/cd /tmpși afișați directorul curent; - explicați ce s-a întâmplat.
Verificare: directorul curent este tot directorul utilizatorului:
/usr/bin/cda rulat ca proces copil, și-a schimbat doar propriul director curent, apoi s-a încheiat. → Comenzi interne și externe - găsiți-l cu
-
Propriul alias:
- adăugați cu
nanoliniaalias lt='ls -lt'la finalul fișierului~/.bashrc; - rulați
ltîn același terminal, apoi într-un terminal nou; - în terminalul vechi, rulați
source ~/.bashrcși încercați din noult; - aflați cu
typece estelt.
Verificare: în terminalul vechi,
ltdă la începutcommand not found:bashcitește~/.bashrcdoar când pornește sau când îi cereți asta cusource; în terminalul nou, și dupăsource,ltlistează fișierele, cele mai noi primele, iartypeafișeazălt is aliased to `ls -lt'. → Comenzi interne și externe - adăugați cu
-
Un mediu gol: Căutați în
man envopțiunea care pornește o comandă cu un mediu gol.- rulați
envcu această opțiune, urmat debash -c 'echo "[$HOME] [$PATH]"'; - rulați
envcu această opțiune, urmat debash -c 'ls'; - rulați
envcu această opțiune, urmat deTZ=Asia/Tokyo /usr/bin/date.
Verificare:
HOMEeste gol, iarPATHare o valoare implicită pe carebasho setează singur, așa călsmerge în continuare;dateafișează ora din Tokyo: singura variabilă pe care a primit-o esteTZ. → Variabile de mediu - rulați
-
PID-uri în background:
- porniți
sleep 300 | sleep 400în background; - afișați
$!și listați job-urile cu PID-urile lor; - încheiați tot job-ul cu un singur
kill, folosind ID-ul de job.
Verificare:
$!este PID-ul luisleep 400, ultimul proces din pipe;jobs -larată ambele PID-uri;kill %1încheie ambele procese, pentru că trimite semnalul întregului grup de procese. → Pornirea unui job · Sesiuni și grupuri de procese - porniți
-
Numărați cu globuri: Mergeți în
/usr/binși, fărălssaufind, folosind doar un glob:- afișați, câte unul pe linie, programele al căror nume începe cu
gși are exact 4 caractere (folosițiprintf '%s\n'); - numărați-le, în aceeași linie de comandă;
- faceți la fel pentru programele al căror nume se termină cu o cifră;
- întoarceți-vă în
~/lab04.
Verificare: primul număr este același cu cel dat de
find /usr/bin -maxdepth 1 -name 'g???' | wc -l; prima listă conținegzipșigrep, iar a treia conținepython3. → Globbing · Pipe-uri - afișați, câte unul pe linie, programele al căror nume începe cu
-
Un alt shell:
- instalați
zshcusudo dnf install zshși porniți-l tastândzsh(dacă afișează un meniu de configurare, apăsați q); - în
~/lab04/glob, rulațiecho *.pdfșiecho docs/**/*.txt; - definiți
NAME="Alice Smith"și rulațiprintf '[%s]\n' $NAME; - ieșiți din
zshcuexit, deschideți un terminal nou (caglobstarsă fie dezactivat), mergeți în~/lab04/glob, definiți din nouNAMEși rulați aceleași trei comenzi înbash.
Verificare:
zshafișeazăzsh: no matches found: *.pdfși nu ruleazăecho; înțelege**fără nicio opțiune și nu împarte$NAMEîn cuvinte.bashafișează*.pdf, doardocs/2026/exam.txt(fărăglobstar,**este*) și două cuvinte. → Globbing · Ghilimele și caracterul\ - instalați
-
Codul de ieșire al unui semnal:
- rulați
sleep 100, opriți-l cu Ctrl+C și afișați codul de ieșire; - faceți la fel cu Ctrl+\;
- rulați
bash -c 'kill -KILL $$'(un shell care își trimite singurSIGKILL) și afișați codul de ieșire.
Verificare: codurile de ieșire sunt
130,131și137: un proces încheiat de un semnal are codul de ieșire128+ numărul semnalului (2,3și9). → Parametri speciali · Semnale - rulați
-
Curățenie:
- încheiați toate procesele
sleepcu o singură comandă (căutați înman pkillcum potriviți numele exact); - ștergeți
~/.local/bin/hello,~/lab04/first,~/lab04/trips,glob/photo4.png,glob/photo5.png,glob/photo6.png,found.txt,list.txt,errors.txt,all.txtșisorted.txtcu o singură comandărm, fără erori pentru cele care nu există (folosiți{...}pentru poze); - eliminați cu
nanoaliasulltde la finalul fișierului~/.bashrc.
Verificare:
pgrep sleepnu afișează nimic,ls ~/lab04arată doar fișierele din exercițiul 1, iarls ~/lab04/globarată aceleași 8 nume ca înainte de exercițiul 46. → Gestionarea fișierelor - încheiați toate procesele
Întrebări de recapitulare
Folosiți ultimele 5 minute ale laboratorului ca să răspundeți la aceste întrebări împreună cu colegii și cu asistentul. La ultima nu există răspunsuri greșite.
- Care este diferența dintre un terminal, un emulator de terminal și un shell?
- Care este diferența dintre
SIGTERMșiSIGKILL? De ce nu poate fi tratatSIGKILL? - Ce se întâmplă când apăsați Ctrl+C, Ctrl+Z și Ctrl+D?
- Care este diferența dintre un ID de job și un PID?
- De ce se încheie job-urile unui terminal când îl închideți? Cum puteți lăsa unul să ruleze în continuare?
- Care este diferența dintre o variabilă de shell și o variabilă de mediu?
- De ce nu poate un proces copil să schimbe o variabilă a părintelui?
- Care este diferența dintre
"$NAME"și'$NAME'? - De ce nu rulează shell-ul un program din directorul curent fără
./? - Care dintre tastele, globurile sau scurtăturile din acest laborator credeți că le veți folosi cel mai des?