Lecția pe scurt
Întrebarea centrală: cum transformi o serie de comenzi tastate într-un terminal într-un mod de lucru pe care îl poți înțelege, verifica, repeta și folosi în siguranță atunci când construiești sau operezi un agent?
În Lecția 5 ai văzut ce se află sub o acțiune aparent simplă: un proces are PID, identitate, grupuri, un folder curent de lucru, descriptori de fișiere și permisiuni. Acum învățăm interfața prin care vei observa și controla toate aceste lucruri în cea mai mare parte a cursului: linia de comandă.
Terminalul nu este „Linux în modul text”, iar shell-ul nu este același lucru cu terminalul. O comandă nu este o propoziție trimisă direct kernelului. Între ceea ce tastezi și procesul care ajunge să ruleze există un interpretor — de obicei un shell — care citește textul, îl descompune, aplică regulile de folosire a ghilimelelor, face expansiuni și redirecționări, caută comanda și abia apoi execută ceea ce ai cerut.
Pentru constructorii de agenți, această distincție este esențială. Un agent de programare, un instrument care pornește subprocese, un serviciu systemd sau o sesiune SSH ajung toate să depindă de aceleași concepte: cale, folder curent, mediu, PATH, intrare și ieșire standard, cod de ieșire, serviciu și log (jurnal de execuție). Dacă nu le înțelegi, poți avea un program care „merge în terminal”, dar eșuează ca serviciu, un script care rulează alt executabil decât crezi, un secret ajuns în istoricul shell-ului sau un lanț de comenzi (pipeline) care pare reușit deși unul dintre pașii lui a eșuat.
Regula lecției
Nu memora comenzi ca pe formule magice. Pentru fiecare comandă trebuie să poți răspunde la cinci întrebări: unde rulează, ce intrare primește, ce proces pornește sau modifică, unde trimite ieșirea și cum afli dacă a reușit.
La finalul lecției vei putea
- să deosebești terminalul, shell-ul, linia de comandă și promptul shell-ului;
- să explici diferența dintre o cale absolută și una relativă și rolul lui
/,~,.și..; - să explici cum găsește shell-ul o comandă și ce rol are variabila
PATH; - să folosești corect ghilimelele, expansiunea variabilelor, expansiunea numelor de fișiere (globbing) și substituția de comandă;
- să diferențiezi
stdin,stdoutșistderrși să urmărești redirecționările; - să explici un pipeline și să nu confunzi textul afișat cu succesul tehnic al întregului lanț;
- să folosești codurile de ieșire și operatorii
&&/||pentru a exprima dependențe între comenzi; - să deosebești variabilele shell-ului de variabilele de mediu și să explici de ce
source .venv/bin/activatemodifică shell-ul curent; - să explici diferența dintre APT,
apt,apt-getșidpkg; - să deosebești
start,enable,restart,reloadșidaemon-reloadîn systemd; - să citești logurile unui serviciu cu
journalctlși să explici de ce logul nu este același lucru cu o urmă de execuție (trace); - să explici arhitectura de bază SSH: client, server, cheie de gazdă (host key), cheie de utilizator,
known_hostsșiauthorized_keys; - să diagnostichezi de ce un program funcționează într-un terminal interactiv, dar eșuează când este pornit ca serviciu sau prin SSH;
- să proiectezi un spațiu de lucru pentru linia de comandă care poate fi recreat și înțeles de altcineva.

1. Terminalul, shell-ul și linia de comandă nu sunt același lucru
Începem cu vocabularul care va reveni în aproape toate lecțiile tehnice următoare.
O aplicație de terminal9 (emulator de terminal) este programul care îți oferă fereastra, textul, cursorul, combinațiile de taste și legătura cu o sesiune de terminal. Konsole, GNOME Terminal și alte aplicații similare oferă interfața de terminal. Ele nu decid singure ce înseamnă cd, export sau |.
Un shell8 este interpretorul de comenzi. În exemplele acestei lecții folosim Bash, un shell foarte răspândit pe Ubuntu și Kubuntu. Alte sisteme pot folosi Zsh, Fish sau alte shell-uri, iar unele reguli de sintaxă diferă. De aceea este mai precis să spui „aceasta este sintaxă Bash” decât „așa funcționează terminalul9”.
Linia de comandă (command line) este modul de interacțiune în care formulezi comenzi textuale. Poți folosi o linie de comandă local, prin SSH25, într-un container, într-o consolă serială sau într-o sarcină automatizată. Nu presupune neapărat o fereastră grafică.
Promptul2 shell-ului8 este textul afișat înainte să tastezi comanda. În Bash, promptul principal este definit de variabila PS1. Nu îl confunda cu un prompt pentru un model lingvistic din Lecțiile 1–2. Sunt două sensuri diferite ale aceluiași cuvânt englezesc.
O analogie scurtă: pupitrul de operare
Poți privi terminalul9 ca pe un pupitru cu ecran și tastatură, iar shell-ul8 ca pe operatorul care interpretează instrucțiunile primite prin acel pupitru. Operatorul decide cum se separă cuvintele, ce înseamnă simbolurile speciale, unde găsește programul cerut și cum leagă ieșirea unei comenzi de intrarea alteia. Kernelul și procesele rămân mecanismele care execută operațiile reale.
2. Vocabularul minim al liniei de comandă
Nu ai nevoie de sute de comenzi. Ai nevoie mai întâi de o hartă a obiectelor și a fluxurilor pe care comenzile le manipulează.
| Termen | Definiție de lucru | Legătura cu lecțiile anterioare |
|---|---|---|
| shell8 | program care interpretează limbajul de comandă și pornește comenzi sau procese | este componenta care lansează și coordonează direct comenzile pornite manual |
| builtin | comandă implementată în shell8, nu neapărat un executabil separat | cd trebuie să modifice chiar shell-ul8 curent |
| executabil | program pe care sistemul îl poate porni | pornirea lui produce un proces |
| argument | valoare transmisă unei comenzi | devine parte din argumentele procesului |
| opțiune | argument interpretat de program ca modificator de comportament | forma exactă aparține programului, nu shell-ului8 |
| CWD16 | folderul curent de lucru al procesului (current working directory) | ai întâlnit deja CWD16 în anatomia procesului din Lecția 5 |
| cale absolută | cale pornită de la rădăcina sistemului de fișiere, / | nu depinde de CWD16 |
| cale relativă | cale interpretată pornind din CWD16 | poate indica alt obiect dacă procesul pornește din alt folder |
stdin | intrarea standard, descriptorul 0 | unul dintre descriptorii standard ai procesului |
stdout | ieșirea standard, descriptorul 1 | poate merge în terminal9, fișier sau pipe13 |
stderr | ieșirea standard pentru diagnostic și erori, descriptorul 2 | este un flux separat de stdout11 |
| pipe13 | canal (pipe13) care leagă ieșirea unei comenzi de intrarea alteia | este un mecanism de comunicare între procese |
| redirecționare | regulă prin care shell-ul8 leagă descriptorii de fișiere sau de alți descriptori | modifică traseul I/O înainte de execuția comenzii |
| cod de ieșire14 | număr prin care comanda indică succesul sau eșecul | poate fi folosit de alt software pentru control determinist |
| variabilă shell8 | nume și valoare existente în shell8 | nu este transmisă automat proceselor-copil |
| variabilă de mediu17 | variabilă transmisă, prin export, în mediul proceselor pornite ulterior | face parte din mediul procesului |
PATH | listă ordonată de foldere în care shell-ul8 caută executabile atunci când numele comenzii nu conține o cale | influențează ce executabil ajunge să fie pornit |
| serviciu22 | proces sau ansamblu de procese administrat ca funcție de sistem | rulează cu propria identitate și propriul mediu |
| unitate systemd21 | obiect gestionat de systemd21: service, socket, timer, mount etc. | descrie o resursă sau un serviciu22 administrat de systemd21 |
| log (jurnal23 de execuție) | înregistrare a unor evenimente produse de sistem sau aplicație | în Lecția 3 l-ai separat de metrici, urme de execuție (traces), teste și evaluări |
| SSH25 | protocol și suită de instrumente3 pentru acces securizat la sisteme la distanță | mută execuția pe alt sistem, fără să schimbe regulile de bază ale proceselor |
- expansiune18 — înlocuire făcută de shell8 înainte de execuție, de exemplu
$HOMEcu valoarea variabilei; - TTY — interfață de terminal9 asociată unei sesiuni; numele vine din istoria teletipurilor;
- grup de procese — mai multe procese pe care shell-ul8 le poate trata împreună, de exemplu într-un pipeline29;
- PID 1 — primul proces din spațiul de procese al sistemului; pe Ubuntu obișnuit acesta este systemd21;
- boot / pornirea sistemului — secvența prin care calculatorul ajunge de la pornire la un sistem Linux funcțional;
- index de pachete — catalog local cu informații despre pachetele disponibile din sursele APT28;
- amprentă (fingerprint) — rezumat scurt al unei chei criptografice, folosit pentru verificare;
- alias — scurtătură definită în shell8, de exemplu alias ll='ls -l';
- funcție de shell8 — un grup de comenzi cu un nume, definit chiar în shell și apelat ca o comandă.
3. Ce face Bash cu textul înainte să pornească programul
Multe dintre surprizele shell-ului8 apar înainte ca programul pe care îl chemi să înceapă să ruleze.
Ia o comandă simplă:
grep -i "$TERMEN" logs/*.log
Nu toate caracterele ajung neschimbate la grep. Bash trebuie mai întâi să interpreteze linia. Pentru un începător este util următorul model mental. Nu este o listă exactă a tuturor etapelor interne din Bash, dar păstrează ordinea conceptuală care te ajută să prezici rezultatul:
$TERMEN, ~, $(...) și modele de nume de fișiere.PATH.&&, ||, scripturi și automatizare.Exemplu concret. Să presupunem că TERMEN conține textul eroare gravă, iar în folderul logs există a.log și b.log. După ce Bash termină interpretarea, grep primește patru argumente: -i, eroare gravă, logs/a.log și logs/b.log. Ghilimelele au dispărut, $TERMEN a fost înlocuit cu valoarea lui, iar logs/*.log a devenit lista fișierelor găsite.
Fiecare pas este explicat mai jos: căutarea comenzii în secțiunea 5, ghilimelele și expansiunile în secțiunea 6, redirecționările în secțiunea 8, codul de ieșire în secțiunea 9.
Această ordine explică de ce două comenzi care arată aproape la fel pot avea efecte diferite. Programul extern vede argumentele după ce shell-ul8 a terminat o parte importantă din interpretare.

4. Căile: cum spune un proces „unde”
Lecția 5 a arătat că accesul depinde de obiect și de folderele traversate. Acum învățăm să descriem acea poziție fără ambiguitate.
Sistemul de fișiere Linux are o rădăcină reprezentată prin /. O cale absolută pornește de la această rădăcină:
/home/ana/ai-agents-zero-to-expert/configs/app.json
O cale relativă este interpretată față de folderul curent de lucru:
configs/app.json
/
├── home/
│ └── ana/
│ └── ai-agents-zero-to-expert/ ← CWD când lucrezi în proiect
│ └── configs/
│ └── app.json
└── tmp/ ← CWD dacă programul pornește de aiciDacă CWD16 este /home/ana/ai-agents-zero-to-expert, cele două pot indica același obiect. Dacă procesul pornește din /tmp, calea relativă indică alt loc sau nu există.
Patru simboluri pe care trebuie să le citești fără ezitare
| Simbol | Înțeles | Exemplu |
|---|---|---|
/ | rădăcina sistemului de fișiere când apare la începutul căii | /var/log |
~ | în Bash, expansiune18 către folderul personal al utilizatorului | ~/project |
. | folderul curent | ./script.sh |
.. | folderul părinte | ../configs |
Consecința practică: ~ funcționează când tastezi în Bash, dar un program care primește textul ~/project, de exemplu dintr-un fișier de configurare, nu îl transformă automat în folderul tău personal.
Folderul curent face parte din starea procesului
pwd afișează folderul curent, iar cd îl schimbă pentru shell-ul8 curent. cd este un builtin important tocmai pentru că un program extern nu poate schimba permanent CWD-ul16 procesului-părinte. Dacă ai porni un executabil separat care își schimbă propriul folder curent, schimbarea s-ar aplica acelui proces, nu shell-ului care l-a pornit.
Aceeași regulă explică multe diferențe între rularea manuală și rularea ca serviciu22. Un program care deschide ./config.json depinde de CWD16. Un serviciu poate porni din alt folder decât terminalul9 tău, deci o cale relativă care „merge la mine” poate eșua imediat.
Calea scrisă și ținta finală pot fi diferite
Din Lecția 5 știi deja că o legătură simbolică (symbolic link) poate indica altă cale. În terminal9, comenzi precum readlink și realpath te ajută să vezi legătura sau să normalizezi o cale. Pentru un agent1 care scrie sau șterge fișiere, diferența contează: validarea unui șir de caractere nu este întotdeauna echivalentă cu validarea obiectului final pe care îl va atinge operația.
5. Comandă, argumente, opțiuni și PATH
Shell-ul8 trebuie să răspundă la două întrebări diferite: „ce argumente îi dau programului?” și „ce program înseamnă primul cuvânt?”.
În comanda:
ls -lah docs/lessons
ls este numele comenzii, iar -lah și docs/lessons sunt argumente. Faptul că -lah poate fi interpretat de ls ca mai multe opțiuni scurte grupate este o regulă a programului ls, nu o regulă universală a shell-ului8.
Înainte de execuție, Bash poate extinde un alias încă din etapa de citire a comenzii. După ce comanda a fost analizată, dacă numele nu conține /, Bash caută în mod simplificat în această ordine: funcție de shell8, comandă internă (builtin), apoi executabil în PATH. Două comenzi utile sunt:
type cd
command -v pythoncd is a shell builtin
/home/ana/ai-agents-zero-to-expert/.venv/bin/pythonA doua linie este un exemplu după activarea mediului virtual într-un proiect aflat în folderul personal al utilizatorului ana; calea ta va fi diferită.
type explică ce vede Bash, iar command -v este potrivit pentru a verifica programatic cum va fi rezolvat un nume de comandă.
Cum afli singur ce face o comandă: man ls deschide manualul complet (ieși cu tasta q), ls --help afișează rezumatul opțiunilor; help cd explică o comandă internă Bash. Când nu știi o opțiune, citește ajutorul înainte s-o cauți pe internet.
Cum funcționează PATH
PATH este o listă de foldere separate prin :. Când tastezi un nume de comandă fără slash, Bash caută un executabil potrivit în aceste foldere, în ordine. De aceea activarea unui mediu virtual Python poate face ca python să indice alt executabil fără să schimbi comanda tastată.
Imaginează-ți că PATH este o listă de sertare pe care shell-ul8 le deschide în ordine. Când ceri python, shell-ul caută în primul folder, apoi în al doilea și tot așa. Primul executabil găsit „câștigă”. De aceea ordinea din PATH schimbă programul care pornește.
Un detaliu important: o componentă goală din PATH poate reprezenta folderul curent. Din motive de siguranță, nu te baza pe această formă.
printf '%s\n' "$PATH"
command -v python
Dacă numele conține slash, de exemplu ./tool sau /usr/bin/python3, ai indicat deja calea și nu mai depinzi de căutarea obișnuită prin PATH.

6. Ghilimele și expansiuni: shell-ul vede mai mult decât textul afișat
Când o comandă primește fișiere, variabile sau date de intrare generate de agent1, folosirea corectă a ghilimelelor (quoting) devine o regulă de corectitudine și securitate, nu un detaliu estetic.
Ghilimele simple, ghilimele duble și backslash
În Bash, ghilimelele simple păstrează conținutul literal:
printf '%s\n' '$HOME'
Comanda va afișa $HOME, nu valoarea variabilei.
$HOME
Ghilimelele duble permit în continuare expansiuni18 precum $VAR și $(...), dar protejează spațiile și multe caractere de separare:
printf '%s\n' "$HOME"
cd "$HOME/Proiect cu spații"/home/anaÎn exemplu presupunem că utilizatorul se numește ana. Comanda cu "$HOME" afișează valoarea variabilei, de exemplu /home/ana. Comanda cd nu afișează nimic dacă folderul există. Dacă folderul nu există, Bash afișează o eroare de tip „No such file or directory”.
Ce se întâmplă fără ghilimele:
cd $HOME/Proiect cu spații
bash: cd: too many argumentsBash a împărțit valoarea în trei cuvinte: /home/ana/Proiect, cu și spații. Comanda cd a primit trei argumente în loc de unul.
Backslash-ul \ poate proteja caracterul următor în anumite contexte. Pentru valori și căi, ghilimelele sunt de obicei mai ușor de citit decât o serie lungă de backslash-uri.
Expansiunea numelor de fișiere (globbing): * nu este filtrul programului
În:
ls logs/*.log
modelul *.log este, în mod obișnuit, expandat de Bash înainte să pornească ls. Programul primește lista de nume rezultată. Această distincție devine importantă când modelul provine dintr-o variabilă sau când numele fișierelor conțin spații.
Dacă logs conține a.log și b.log, ls primește logs/a.log și logs/b.log. Dacă nu există niciun fișier .json, ls logs/*.json primește chiar textul logs/*.json și răspunde că nu îl găsește.
În configurația implicită Bash, un model glob fără potriviri poate rămâne text literal; opțiunile shell-ului8 pot schimba acest comportament. De aceea nu construi logică critică presupunând că *.json devine întotdeauna o listă nevidă.
Substituția de comandă $(...)
Expresia:
ROOT="$(pwd)"
rulează pwd și folosește ieșirea sa standard ca valoare. Ai folosit deja această tehnică în laboratoare. Ghilimelele din jurul expresiei păstrează rezultatul ca un singur argument chiar dacă ar conține spații. Bash elimină însă liniile noi de la finalul rezultatului substituției de comandă20; de aceea $(...) nu este o metodă potrivită pentru a păstra identic orice flux de octeți.
"$VAR", nu $VAR. Expansiunea18 neprotejată poate permite separarea în mai multe cuvinte și expandarea neintenționată a numelor de fișiere.7. Trei fluxuri standard: stdin, stdout și stderr
În Lecția 5 ai întâlnit descriptori de fișiere. Aici folosim cei trei descriptori pe care aproape orice proces de linie de comandă îi primește la pornire.
| Descriptor | Nume | Rol uzual |
|---|---|---|
0 | stdin | date de intrare |
1 | stdout | rezultat normal |
2 | stderr | diagnostic și erori |
Într-o sesiune interactivă, toate trei pot fi conectate la terminal9. Asta face să pară că „totul este același text”. Nu este. Shell-ul8 poate conecta fiecare descriptor separat.
De exemplu, un program poate scrie un rezultat JSON pe stdout11 și un mesaj de progres pe stderr12. Alt program poate consuma stdout fără ca mesajele de diagnostic să intre în date.

8. Redirecționări și pipe-uri: compunerea comenzilor
Puterea liniei de comandă nu vine numai din numărul de utilitare, ci din faptul că le poți compune prin fluxuri bine definite.
Redirecționările mută descriptorii
Operatorul > redirecționează stdout11 către un fișier și, în mod obișnuit, îl creează sau îl golește înainte să scrie:
În exemplele de mai jos, comanda_ta ține locul oricărei comenzi. Nu tasta cuvântul command: în Bash este chiar o comandă internă, cu alt rol.
comanda_ta > output.txt
>> adaugă la final:
comanda_ta >> output.txt
2> redirecționează stderr12:
comanda_ta 2> errors.txt
Pentru a trimite ambele fluxuri în același loc:
comanda_ta > all.txt 2>&1
Ordinea contează. Bash aplică redirecționările de la stânga la dreapta. 2>&1 înseamnă „fă descriptorul 2 să indice unde indică descriptorul 1 în acel moment”. De aceea mutarea operatorilor poate schimba rezultatul. Comparația: comanda_ta > all.txt 2>&1 trimite ambele fluxuri în fișier. comanda_ta 2>&1 > all.txt trimite stdout11 în fișier, dar stderr12 rămâne pe ecran, pentru că în momentul în care Bash a aplicat 2>&1, descriptorul 1 încă indica terminalul9.
Pipe-ul transmite stdout către stdin
| sau |&. În această secțiune folosim sensul Bash.printf '%s\n' error ok error | sort | uniq -c 2 error
1 ok
Ieșirea standard a primei comenzi intră în a doua, iar ieșirea celei de-a doua intră în a treia. Un pipe13 Bash obișnuit | nu include automat stderr12. Bash are și operatorul |&, care conectează stdout11 și stderr ale comenzii din stânga la stdin-ul10 următoarei, dar este mai clar să îl folosești numai când chiar vrei ambele fluxuri.
Un utilitar precum tee poate copia același flux spre un fișier și, simultan, spre stdout11:
comanda_ta | tee run.log
Este util pentru observație, dar nu transforma orice pipeline29 într-un sistem de logare. Serviciile au mecanisme dedicate, iar logurile pot conține date sensibile.
9. Codul de ieșire: cum comunică o comandă succesul
Textul afișat este pentru om; codul de ieșire este semnalul simplu pe care îl poate folosi alt software.
Prin convenție, 0 indică succes, iar o valoare nenulă indică un tip de eșec sau o stare4 specială definită de program.
ls fisier-inexistent
printf '%s\n' "$?"ls: cannot access 'fisier-inexistent': No such file or directory
2Mesajul exact de eroare poate varia în funcție de limba sistemului. Pe Ubuntu, GNU ls folosește codul 2 pentru o eroare serioasă de acest tip.
$? conține codul ultimei comenzi sau al ultimei structuri evaluate de shell8. Trebuie citit imediat; următoarea comandă îl înlocuiește.
Operatorii &&, || și ;
| Operator | Comportament | Utilizare |
|---|---|---|
A && B | rulează B numai dacă A a reușit | nu continua dacă verificarea a eșuat |
A || B | rulează B dacă A a eșuat | variantă de rezervă sau mesaj de eroare |
A ; B | rulează B indiferent de codul lui A | secvență în care pașii nu depind unul de altul |
Pentru automatizări, && exprimă intenția mai bine decât două comenzi independente atunci când pasul al doilea depinde de primul.
Capcana lanțului de comenzi (pipeline)
În Bash, fără opțiunea pipefail, codul de ieșire al unui pipeline29 este în mod normal codul ultimei comenzi. Asta înseamnă că prima comandă poate eșua, iar ultima poate ieși cu 0.
În scripturi în care succesul întregului lanț contează, poți activa:
set -o pipefail
Cu pipefail, un eșec din pipeline29 poate produce un cod nenul pentru întregul pipeline: Bash folosește codul ultimei comenzi din lanț care a ieșit cu o valoare nenulă. Dacă toate comenzile reușesc, rezultatul este 0.
pipefail și set -e schimbă semantica shell-ului8 și trebuie înțelese și testate. În laborator vom folosi explicit numai comportamentele pe care le putem explica.10. Variabile shell, variabile de mediu și export
PATH sau LOG_LEVEL. Sunt două concepte diferite.Multe configurații ale agenților1 — adrese de serviciu22 (endpointuri), nivel de logare, căi, comutatoare de funcționalități (feature flags) — ajung în mediu. Dar „este într-o variabilă” nu ne spune încă dacă un subproces o va primi.
În Bash:
MODEL_URL="http://127.0.0.1:8000"
creează o variabilă a shell-ului8. Pentru a o include în mediul proceselor executate ulterior:
export MODEL_URL
sau direct:
export MODEL_URL="http://127.0.0.1:8000"
Poți furniza o variabilă numai pentru o singură comandă:
LOG_LEVEL=debug comanda_ta
În acest caz, valoarea este disponibilă comenzii executate, fără să devină neapărat o setare permanentă pentru comenzile următoare.
Mediul se transmite către subprocese (procese-copil), nu înapoi către părinte
Un subproces (proces-copil) primește la pornire mediul exportat de procesul-părinte. Dacă procesul-copil își schimbă propriul mediu, acea schimbare nu rescrie mediul părintelui.
Procesul-părinte îi dă procesului-copil un „rucsac” cu variabilele exportate. Copilul poate schimba ce are în propriul rucsac, dar nu poate pune acele schimbări înapoi în rucsacul părintelui.
Aceasta explică de ce mediul virtual Python este activat astfel:
source .venv/bin/activate
source este builtin Bash și execută fișierul în shell-ul8 curent. Scriptul de activare modifică PATH și alte variabile ale acelei sesiuni. Dacă l-ai porni într-un proces separat, schimbările de mediu nu s-ar întoarce în shell-ul părinte.
Un fișier .env nu este încă o variabilă de mediu
Este o confuzie frecventă în proiectele web și AI. Bash nu încarcă automat un fișier numit .env doar pentru că acesta există. Anumite framework-uri și biblioteci îl citesc explicit. Alte aplicații nu îl cunosc deloc.
Prin urmare, propoziția „cheia este în .env” nu răspunde la întrebarea „cum ajunge cheia în proces?”. Trebuie să știi cine citește fișierul, când îl citește și cu ce permisiuni.

11. Managerii de pachete: instalează software-ul în stratul potrivit
În Lecția 3 ai separat runtime-ul, framework-ul și aplicația. Managerii de pachete respectă aceeași idee: nu toate pachetele aparțin aceluiași strat și nu toate trebuie instalate cu același instrument3.
APT și dpkg pe Ubuntu/Kubuntu
APT28 este sistemul de administrare a pachetelor Debian folosit de Ubuntu. Comanda apt oferă o interfață comodă pentru utilizator:
sudo apt update
sudo apt install jq
apt show jq
apt update actualizează indexul local al pachetelor disponibile. Nu actualizează pachetele deja instalate. apt upgrade este operația care încearcă să actualizeze pachetele instalate în limitele politicii APT28.
Documentația Ubuntu recomandă apt pentru utilizare interactivă, iar apt-get pentru scripturi și automatizări unde este de preferat o interfață mai stabilă. Pentru multe operații de bază, comenzile seamănă, dar scopul interfețelor nu este identic.
dpkg lucrează la un nivel mai jos cu pachetele Debian locale și baza de date a pachetelor. Poate instala un fișier .deb, dar nu are singur comportamentul APT28 de a descărca și rezolva automat toate dependențele din surse de pachete.
apt
Interfață orientată către utilizator pentru surse de pachete, instalare, actualizare și inspecție.
apt-get
Interfață APT28 potrivită pentru scripting și automatizare.
dpkg
Operații asupra pachetelor Debian locale și bazei dpkg; nu înlocuiește rezolvarea dependențelor din surse de pachete.
Pachet de sistem versus pachet al limbajului
Mai târziu, în Lecția 7, vei folosi managerul de pachete al ecosistemului Python. Asta nu înseamnă că APT28 și pip sunt două nume pentru același lucru. APT administrează pachete integrate în sistemul Ubuntu; pip administrează distribuții Python într-un mediu Python.
Pentru proiectele reproductibile, regula sănătoasă este să alegi conștient stratul: instrument3 de sistem prin managerul sistemului, dependență Python în mediul Python, dependență Node în proiectul Node etc. Evită să rezolvi orice eroare de instalare prin creșterea privilegiilor.
12. Prim-plan, fundal și sarcini: cum organizează shell-ul procesele pornite de tine
Lecția 5 a tratat procesul ca obiect al sistemului de operare. Aici adăugăm perspectiva shell-ului8 interactiv.
În mod normal, când pornești o comandă în prim-plan (foreground), shell-ul8 așteaptă terminarea ei înainte să îți redea promptul2. Dacă adaugi &, o pornești ca sarcină în fundal (background) al shell-ului:
long_command &
jobs arată sarcinile urmărite de shell-ul8 curent, iar fg și bg le pot muta între prim-plan și fundal. Identificatorul unei sarcini de shell nu este același lucru cu PID-ul: o singură sarcină poate cuprinde unul sau mai multe procese.
& este doar o sarcină a sesiunii shell8. Pentru procese care trebuie administrate, repornite și pornite controlat la pornirea sistemului (boot), un manager de servicii precum systemd21 este instrumentul3 potrivit.13. systemd: de la proces lansat manual la serviciu administrat
Pe Ubuntu și Kubuntu actuale, systemd21 este managerul de sistem și servicii22. Ca manager de sistem, rulează ca PID 1; există și instanțe systemd pentru servicii ale utilizatorului.
systemd21 nu administrează doar servicii22. Conceptul general este unitatea (unit): un obiect precum .service, .socket, .timer, .mount sau .target. În această lecție ne concentrăm pe unități de tip .service.
Pornirea (start) nu este același lucru cu activarea (enable)
| Comandă | Ce face | Ce NU înseamnă |
|---|---|---|
systemctl start app.service | pornește unitatea acum | nu o configurează automat pentru activare viitoare |
systemctl stop app.service | oprește unitatea acum | nu îi schimbă automat configurația de activare |
systemctl enable app.service | creează legăturile definite de configurația de instalare pentru activare viitoare | nu pornește automat unitatea în acel moment |
systemctl enable --now app.service | configurează activarea și pornește unitatea acum | nu dovedește că aplicația este configurată corect |
systemctl restart app.service | oprește și pornește din nou unitatea | nu este același lucru cu reload |
systemctl reload app.service | cere serviciului să își reîncarce propria configurație, dacă suportă operația | nu recitește fișierul de definiție al unității systemd21 |
systemctl daemon-reload | cere managerului systemd21 să recitească definițiile unităților | nu reîncarcă automat configurația internă a aplicației |
Această separare este explicită în documentația systemd21: enable și start sunt operații distincte. Un serviciu22 poate fi pornit acum, dar disabled pentru activare la pornirea sistemului (boot); poate fi enabled, dar momentan oprit.
Comanda status oferă o fotografie de moment, nu un diagnostic complet
systemctl status app.service
arată starea4 unității și câteva informații recente. Este un punct bun de început, dar nu este un substitut pentru loguri, fișierul de definiție al unității și configurația aplicației.
Pentru servicii22 ale utilizatorului există și:
systemctl --user status app.service
Acesta comunică cu instanța systemd21 a utilizatorului, nu cu managerul de sistem. Separarea contează pentru identitate, permisiuni și locurile din care sunt încărcate unitățile.

14. Logurile: urmărește ce s-a întâmplat, nu ghici
În Lecția 3 ai separat logurile de metrici, urmele de execuție (traces), teste și evaluări. Acum folosim logurile sistemului pentru a vedea ce a făcut un serviciu22.
Jurnalul23 (journal) este colecția de loguri pe care o păstrează journald.
systemd21-journald colectează mesaje din mai multe surse, inclusiv mesaje ale kernelului, syslog și stdout11/stderr12 ale serviciilor systemd. journalctl interoghează acest jurnal23 structurat.
Comenzi de orientare:
journalctl -u app.service
journalctl -u app.service -n 50
journalctl -u app.service -f
journalctl -u app.service --since "10 minutes ago"
journalctl -b
journalctl -p err
-u filtrează după unitate; -f urmărește intrările noi; -b limitează rezultatul la o pornire a sistemului (boot); --since restrânge intervalul; -p filtrează după prioritate.
Logurile pot fi persistente sau volatile
De aceea nu presupune că „journalctl24 are sigur logurile de ieri”. Mai întâi verifică politica de stocare și perioada pentru care există date.
Log versus trace
Un log consemnează un eveniment: serviciul22 a pornit, un fișier lipsește, o cerere a eșuat. O urmă de execuție (trace) leagă pașii aceleiași operații prin mai multe componente. Un agent1 poate avea loguri excelente și totuși să fie greu de urmărit de la un capăt la altul dacă nu există un identificator sau alt mecanism de corelare între pași.

15. SSH: execuția se mută pe alt sistem, regulile nu dispar
SSH25 este una dintre principalele căi prin care vei opera servere, sisteme cu GPU, mașini virtuale și alte medii în care rulează agenți1.
În forma cea mai simplă:
ssh user@hostuser@host: user este numele contului de pe sistemul la distanță, iar host este numele sau adresa acelui sistem. Cele două nu descriu calculatorul local.Clientul SSH25 local creează o conexiune criptată către serverul SSH de pe sistemul la distanță. După autentificare, serverul poate porni un shell8 interactiv sau o comandă specifică.
ssh user@host 'systemctl --user status agent.service'
A doua formă nu înseamnă neapărat „deschid un terminal9 complet”. Serverul execută acea comandă într-o sesiune la distanță. În mod obișnuit, comanda ajunge la shell-ul8 utilizatorului într-un context neinteractiv, iar fișierele de inițializare și mediul pot diferi de cele ale unei sesiuni interactive. De aceea o comandă care merge după autentificare interactivă poate eșua când este trimisă direct prin ssh host 'comandă'.
Două verificări diferite: serverul și utilizatorul
„Este acesta serverul corect?”
Clientul verifică cheia de gazdă26 (host key) a serverului. Cheile cunoscute sunt păstrate de obicei în ~/.ssh/known_hosts.
„Are acest utilizator voie să intre?”
Serverul autentifică utilizatorul. În autentificarea cu cheie publică, cheia publică autorizată este listată de obicei în ~/.ssh/authorized_keys pe sistemul la distanță, iar cheia privată rămâne la client.
Cheia privată este autoritate
Cheia privată nu pleacă niciodată de pe calculatorul tău. Serverul are nevoie numai de cheia publică autorizată; clientul dovedește că deține cheia privată fără să o trimită.

16. Exemplu complet — „merge în terminal, dar serviciul eșuează”
Acesta este unul dintre cele mai utile exerciții de diagnostic pentru un constructor de agenți1, deoarece leagă căile, mediul, PATH15, permisiunile, serviciile și logurile într-o singură problemă.
Presupunem că ai un program fictiv document-agent. Pornit manual din proiect, funcționează:
cd ~/ai-agents-zero-to-expert
source .venv/bin/activate
export MODEL_URL="http://127.0.0.1:8000"
python src/document_agent.py
Îl transformi într-un serviciu22 systemd21 și primești o eroare. În loc să adaugi sudo sau să reinstalezi pachete, urmărești diferențele de context.
./configs/app.json? Este definit WorkingDirectory?python din terminal venea din .venv/bin? Serviciul folosește aceeași cale?MODEL_URL era exportat numai în shell-ul interactiv? Unitatea îl primește explicit?journalctl -u document-agent.service arată un fișier lipsă, un executabil greșit sau o variabilă absentă?daemon-reload îl face pe systemd să îl recitească înainte de restart.Observă că niciuna dintre aceste cauze nu este „AI”. Sunt proprietăți ale procesului și mediului Linux. Tocmai de aceea fundațiile software apar înainte de Python, API-uri5 și framework-uri pentru agenți1 în planul cursului.
Diagnostic în limbaj natural
Simptom: manual funcționează; serviciul22 nu găsește executabilul Python așteptat.
Ipoteză: shell-ul8 interactiv avea .venv/bin în PATH15 după source .venv/bin/activate; serviciul22 nu primește automat același PATH.
Dovadă: compari command -v python în shell8 cu ExecStart și mediul definit pentru unitate.
Corecție: folosești o cale explicită, precum /cale/proiect/.venv/bin/python, în configurația serviciului sau definești mediul într-un mod controlat.

17. Securitate și confidențialitate în linia de comandă
Pentru un agent1, shell-ul8 este un instrument3 foarte puternic tocmai pentru că poate porni aproape orice program accesibil identității procesului. Puterea trebuie delimitată.
Nu pune secretele în locuri pe care instrumentele de diagnostic le pot expune
O comandă tastată poate ajunge în istoricul shell-ului8. Argumentele unui proces pot fi vizibile prin instrumente3 de inspecție, în funcție de permisiuni și configurația sistemului. Logurile pot înregistra comenzi sau valori din mediu. De aceea:
- nu pune chei API5 direct în comenzi pe care le vei copia în chat, tichet de lucru sau jurnal23;
- nu presupune că
export SECRET=...este management de secrete; - nu tipări întregul mediu al procesului pentru diagnostic pe un sistem cu credențiale;
- nu salva ieșirea de depanare fără s-o inspectezi înainte de commit sau încărcare.
Datele de intrare provenite din surse care nu sunt de încredere nu trebuie transformate în sintaxă shell
Dacă un agent1 primește un nume de fișier sau o propoziție de la utilizator și construiește o comandă shell8 lipind textul primit direct în comandă (concatenare), caracterele speciale pot fi reinterpretate de shell. În cod, preferă API-uri5 care transmit argumentele ca valori separate și evită shell-ul atunci când nu ai nevoie de sintaxa lui.
Exemplu: agentul1 construiește comanda cat + numele primit. Dacă numele este raport.txt; echo SURPRIZĂ, shell-ul8 vede două comenzi și o execută și pe a doua. Cu un text rău intenționat, a doua comandă ar putea șterge sau trimite date.
În Python vom reveni la acest lucru cu exemple concrete. Pentru moment reține principiul: datele utilizatorului sunt date; nu le transforma accidental în cod sau sintaxă de shell8.
Sudo rămâne o limită de privilegiu
Lecția 5 a stabilit deja că sudo nu este remediul universal pentru „Permission denied”. În linia de comandă, regula rămâne aceeași: verifică identitatea, proprietarul, grupul și operația înainte să crești privilegiul.
Un agent1 care poate executa sudo fără o politică strictă nu mai are doar acces la shell8; are și o cale de creștere a privilegiilor. Dacă o operație administrativă este necesară, proiectează o interfață îngustă, validată și auditată.
18. Harta instrumentelor: ce responsabilitate are fiecare
Ca în Lecția 4, nu memorăm nume fără categorie. În această lecție categoria este mai importantă decât numele utilitarului.
Ce este: Bash — shell8 și limbaj de comandă.
Ce problemă rezolvă: interpretează textul tastat sau primit: expandează variabilele, rezolvă căile, pornește programele și leagă fluxurile lor.
Când ajungi la el: când orice comandă trebuie pornită și compusă — în terminal9 sau din scripturile agentului1.
Comandă exemplu și ce arată: echo $0 afișează shell-ul8 care rulează acum (real: -bash într-o sesiune de login, bash într-un terminal9 obișnuit deschis din interfața grafică; într-un script, $0 este numele scriptului); confirmă cine interpretează comenzile tale.
Ce NU este și cu ce se confundă: nu este aplicația de terminal9 (cea care desenează fereastra) și nu este kernelul; se confundă cu ambele pentru că „se văd în aceeași fereastră”.
Ce este: GNU Coreutils — utilitare fundamentale precum ls, cp, mv, sort, wc.
Ce problemă rezolvă: operațiile elementare peste fișiere și text, cu interfețe stabile și documentate — cărămizile fiecărui script.
Când ajungi la el: când sarcina cere listare, copiere, sortare sau numărare — înainte să scrii cod propriu pentru ceva ce există deja.
Comandă exemplu și ce arată: printf '%s
' a b c | wc -l afișează 3 — real, rulat pe Ubuntu 24.04: printf scrie trei linii, wc -l le numără; arată compunerea a două utilitare prin pipe13.
Ce NU este și cu ce se confundă: nu este manager de servicii22; se confundă cu shell-ul8 — utilitarele sunt pornite de shell, nu fac parte din el.
Ce este: findutils / find — căutare și selecție de obiecte din sistemul de fișiere.
Ce problemă rezolvă: găsirea fișierelor după nume, tip, vârstă sau dimensiune, cu acțiuni opționale asupra rezultatelor.
Când ajungi la el: când cauți „fișierele create ieri în folderul X” — o interogare pe structura discului, nu pe conținut.
Comandă exemplu și ce arată: find /tmp/l5q -name 'document' — real, pe structura din Lecția 5: /tmp/l5q/x/y/document; arată calea completă a obiectului găsit.
Ce NU este și cu ce se confundă: nu este index semantic — caută nume și atribute, nu sens; se confundă cu căutarea „în conținut”, care este treaba lui grep sau a unui motor de retrieval.
Ce este: grep — căutare de modele de text, linie cu linie.
Ce problemă rezolvă: găsirea rapidă a liniilor care se potrivesc unui tipar, în fișiere sau în fluxuri primite prin pipe13.
Când ajungi la el: când vrei să filtrezi ieșirea unei comenzi sau să cauți un cuvânt în loguri — de pildă erorile dintr-un jurnal23.
Comandă exemplu și ce arată: echo "text demo" | grep -c t afișează 1 — real pe Ubuntu 24.04: grep -c numără liniile cu potriviri; arată filtrarea prin pipe13.
Ce NU este și cu ce se confundă: nu este analizor universal pentru JSON sau loguri structurate; se confundă cu un parser — tiparele de text nu înțeleg structura.
Ce este: less — program de paginare pentru citirea ieșirii lungi.
Ce problemă rezolvă: citirea confortabilă a textului care depășește ecranul, cu navigare și căutare, fără să modifice nimic.
Când ajungi la el: când o ieșire (de pildă journalctl sau un fișier lung) este mai mare decât ecranul și vrei să o parcurgi.
Comandă exemplu și ce arată: journalctl | less — deschide jurnalul23 în paginare: primele rânduri ale jurnalului apar, iar q iese; arată că ieșirea unei comenzi poate deveni intrarea alteia.
Ce NU este și cu ce se confundă: nu modifică sursa — este doar vizualizator; se confundă cu un editor, dar ce vezi în less nu se salvează nicăieri.
Ce este: APT28 — administrarea pachetelor Debian și a surselor de pachete pe Ubuntu.
Ce problemă rezolvă: instalarea, actualizarea și scoaterea pachetelor de sistem, cu rezolvarea dependențelor dintre ele.
Când ajungi la el: când un instrument3 de sistem lipsește — graphviz, acl, pciutils — și trebuie instalat la nivelul potrivit al stivei.
Comandă exemplu și ce arată: apt list --upgradable 2>/dev/null | head -3 — real, pe Ubuntu 24.04: listează pachetele cu versiuni noi după apt update; arată că indexul și pachetele sunt operații separate.
Ce NU este și cu ce se confundă: nu este managerul dependențelor Python; se confundă cu pip — APT28 instalează pachete de sistem, pip pachete de limbaj în mediu virtual.
Ce este: dpkg — operații asupra pachetelor Debian locale și a bazei dpkg.
Ce problemă rezolvă: inspecția și manipularea pachetelor deja prezente: ce este instalat, ce fișiere aparțin unui pachet, ce versiuni există.
Când ajungi la el: când vrei să vezi ce pachet a instalat un fișier sau ce versiune exactă este pe sistem — fără descărcări.
Comandă exemplu și ce arată: dpkg -l | grep pciutils — real, pe Ubuntu 24.04: ii pciutils …; ii înseamnă pachet instalat corect, iar numele arată că utilitarul lspci provine de aici.
Ce NU este și cu ce se confundă: nu rezolvă singur toate dependențele din surse de pachete; se confundă cu APT28 — dpkg lucrează pe pachete locale, APT le aduce și le rezolvă.
Ce este: systemd21 / systemctl — management de servicii22 și alte unități.
Ce problemă rezolvă: transformarea unui proces oarecare într-un serviciu22 administrat: pornire la boot, repornire la eșec, stare4, jurnal23.
Când ajungi la el: când agentul1 nu mai este experiment în terminal9 și trebuie să fie pornit, supravegheat și oprit ca serviciu22 (secțiunea 13).
Comandă exemplu și ce arată: systemctl is-enabled agent.service și systemctl is-active agent.service — două comenzi care răspund la două întrebări diferite: configurare pentru boot și stare4 curentă (real: enabled și inactive până la primul start).
Ce NU este și cu ce se confundă: nu este shell8; se confundă cu comenzile manuale — systemd21 programează și supraveghează, shell-ul doar pornește odată.
Ce este: journald / journalctl — colectare și interogare de loguri structurate.
Ce problemă rezolvă: adunarea a ceea ce scriu serviciile (inclusiv stdout11/stderr12) plus metadate (dată, unitate, PID), căutabile ulterior.
Când ajungi la el: când agentul1 e servit de systemd21 și vrei să vezi ce a scris, căutat pe unitate sau pe interval de timp.
Comandă exemplu și ce arată: journalctl -u agent.service afișează doar înregistrările unității respective — fiecare rând are dată, unitate și text; căutarea pe unitate înlocuiește scroll-ul prin tot jurnalul23.
Ce NU este și cu ce se confundă: nu este un sistem complet de urmărire distribuită (distributed tracing); se confundă cu trace-ul — jurnalul23 înregistrează evenimente locale, nu traseul unei cereri prin mai multe componente.
Ce este: OpenSSH / ssh — conectare și execuție securizată la distanță.
Ce problemă rezolvă: mutarea execuției pe alt sistem, cu autentificare și criptare: comenzi, fișiere și porturi, în ambele direcții controlate.
Când ajungi la el: când agentul1 sau scripturile trebuie să ruleze pe server, iar contextul de execuție de acolo (secțiunea 16) trebuie inspectat pe loc.
Comandă exemplu și ce arată: ssh host 'command -v python3' arată ce rezolvare de Python are sistemul la distanță — adesea diferită de cea locală; confirmă contextul, nu-l presupune.
Ce NU este și cu ce se confundă: nu transferă automat identitatea și mediul local; se confundă cu sesiunea locală — la distanță, PATH-ul15, CWD-ul16 și variabilele sunt ale sistemului de acolo.
19. Trei familii de greșeli care apar repetat
Greșelile sunt grupate după cauză, nu după numele comenzii. Astfel le poți recunoaște și într-un instrument3 nou.
Fiecare card pornește de la o propoziție pe care o spune des cineva care învață. Citește-o, încearcă să vezi singur problema, apoi citește explicația.
19.1 Greșeli de context: calea, CWD-ul și mediul sunt presupuse
Ce spune cineva: „Fișierul există, deci serviciul22 îl va găsi.”
De ce este greșit: o cale relativă depinde de CWD16. Întrebarea corectă: din ce folder rulează procesul și ce cale finală încearcă să deschidă?
Ce se întâmplă de fapt: calea relativă se interpretează pornind din CWD-ul16 procesului. Comenzi reale care arată diferența, rulate pe Ubuntu 24.04:
pwd
/tmp/serviciu-simulat
ls -l configs/app.json
-rw-r--r-- 1 ai ai 0 oct 17 10:20 configs/app.jsonFișierul există la /tmp/serviciu-simulat/configs/app.json; un serviciu22 pornit din /var/lib/agent va căuta /var/lib/agent/configs/app.json — inexistent. Confirmarea se face cu pwd în contextul procesului și cu ls -l pe calea completă rezultată.
Ce spune cineva: „Am exportat variabila în terminal9, deci serviciul22 o are.”
De ce este greșit: mediul shell-ului8 tău nu este automat mediul unui serviciu22 systemd21. Întrebarea corectă: cine pornește procesul și ce mediu îi furnizează?
Ce se întâmplă de fapt: mediul merge numai de la părinte la copil. Exportul din terminalul9 tău ajunge în procesele pornite de acel terminal; un serviciu22 pornit de systemd21 primește mediul definit în fișierul lui unit. Confirmarea reală se face pe proces: tr "\0" "\n" < /proc/PID/environ afișează mediul efectiv al procesului — ce vezi acolo este exact ce are, nu ce ai exportat tu în altă fereastră.
Ce spune cineva: „python este întotdeauna același executabil.”
De ce este greșit: PATH15 și mediile virtuale pot schimba rezolvarea. Întrebarea corectă: ce arată command -v python în contextul care contează?
Ce se întâmplă de fapt: rezolvarea numelui depinde de PATH15 și de mediile virtuale. Confirmare reală pe Ubuntu 24.04:
command -v python3
/usr/bin/python3
.venv/bin/python3 --version
Python 3.12.3Același nume cere două executabile diferite în două contexte — /usr/bin/python3 pentru sistem, .venv/bin/python3 pentru mediul virtual activ. Întrebarea corectă se răspunde cu command -v rulat în contextul procesului care contează.
19.2 Greșeli de flux: stdout, stderr și codul de ieșire sunt confundate
Ce spune cineva: „Nu a afișat nimic, deci a reușit.”
De ce este greșit: succesul este comunicat prin codul de ieșire, iar ieșirea poate fi redirecționată. Întrebarea corectă: ce cod de ieșire14 a returnat?
Ce se întâmplă de fapt: codul de ieșire spune rezultatul, nu tăcerea. Demonstrație reală:
ls fisier-inexistent 2> /dev/null; echo $?
2Terminalul9 a rămas complet curat — eroarea a fost trimisă în /dev/null —, dar codul de ieșire 2 arată eșecul. Confirmarea este echo $? imediat după comandă.
Ce spune cineva: „Pipe-ul13 trimite automat și erorile.”
De ce este greșit: | conectează stdout11 la stdin10; stderr12 rămâne separat dacă nu îl redirecționezi. Întrebarea corectă: ce descriptor vreau să conectez?
Ce se întâmplă de fapt: erorile țin de fluxul 2, care nu intră în pipe13. Demonstrație reală:
ls fisier-inexistent | wc -l
ls: cannot access 'fisier-inexistent': No such file or directory
0Eroarea apare în terminal9 (nu a intrat în pipe13), iar wc -l a primit zero linii. Confirmarea: aceeași comandă cu 2>&1 | wc -l — fluxurile combinate produc o altă numărare, arătând că descriptorii merg pe drumuri diferite.
Ce spune cineva: „Ultima comandă din pipeline29 a reușit, deci tot pipeline-ul a reușit.”
De ce este incomplet: fără pipefail, Bash raportează în mod normal statusul ultimei comenzi. Întrebarea corectă: cum tratează scriptul eșecurile intermediare?
Ce se întâmplă de fapt: fără set -o pipefail, Bash raportează statusul ultimei comenzi din pipe13. Confirmare reală: grep -c potrivire fisier-inexistent | wc -l — grep eșuează (fișier inexistent), dar wc -l reușește și pipeline-ul29 raportează succesul ultimei comenzi, mascând eșecul din amonte. Cu set -o pipefail înainte, același lanț raportează eșecul primului pas.
19.3 Greșeli de operare: pachet, serviciu și acces la distanță
Ce spune cineva: „apt update mi-a actualizat toate programele.”
De ce este greșit: actualizează indexul local al surselor de pachete. Întrebarea corectă: am actualizat indexul sau pachetele instalate?
Ce se întâmplă de fapt: apt update actualizează numai indexul — lista „ce există și de unde se descarcă”. Confirmare reală pe Ubuntu 24.04:
apt list --upgradable 2>/dev/null | head -3
Se înnoiesc…Comanda arată ce pachete au versiuni noi după actualizarea indexului — instalarea lor este apt upgrade, o operație separată. Confirmarea: apt policy nume-pachet arată versiunea instalată versus candidat.
Ce spune cineva: „Am dat enable, deci serviciul22 rulează.”
De ce este greșit: enable și start descriu lucruri diferite. Întrebarea corectă: este unitatea activă acum și este configurată pentru activare viitoare (enabled)?
Ce se întâmplă de fapt: enable programează pornirea la boot; starea4 curentă se citește separat. Confirmare reală cu systemctl is-enabled agent.service (răspunde enabled) și systemctl is-active agent.service (răspunde inactive până la primul start) — două comenzi, două întrebări diferite.
Ce spune cineva: „Cheia de gazdă26 s-a schimbat; șterg avertismentul și continui.”
De ce este periculos: schimbarea poate fi legitimă sau poate indica impersonare. Întrebarea corectă: pot verifica printr-un canal de încredere amprenta (fingerprint) noii chei de gazdă26?
Ce se întâmplă de fapt: cheia de gazdă26 este amprenta serverului; schimbarea ei merită confirmată printr-un canal de încredere (consolă de la furnizor, un coleg cu acces) înainte de a continua. Ștergerea liniei din known_hosts cu ssh-keygen -R host este legitimă doar după confirmarea motivului — altfel avertismentul următor (posibil o impersonare) este tratat la fel ca primul.
20. Găsește greșeala — „DevOps pentru agenți în 30 de secunde”
Exercițiul combină confuziile lecției într-un singur „tutorial” intenționat prost.
„Dacă agentul1 nu pornește, fă așa: intră cu SSH25, ignoră avertismentul despre cheia de gazdă26, rulează tot cu sudo27, pune folderul curent la începutul PATH15-ului, exportă toate cheile API5 într-o singură variabilă, apoi execută
apt updateca să actualizezi toate programele. Dăsystemctl enable agent.serviceși serviciul22 va porni imediat. Dacă nu vezi nimic în terminal9, înseamnă că a mers. Pentru depanare, salvează întregul mediu al procesului și toate prompturile în log.”
Cheia instructorului
1. „Dacă agentul1 nu pornește, fă așa: intră cu SSH25…” SSH (Secure Shell8) este protocolul prin care execuți comenzi pe alt sistem de la distanță (secțiunea 15). Conexiunea la distanță în sine este legitimă — problema nu este SSH-ul, ci tot ce urmează în aceeași listă: fiecare pas de mai jos așază o decizie periculoasă în locul unei verificări.
2. „…ignoră avertismentul despre cheia de gazdă26…” Cheia de gazdă (host key) este amprenta publică prin care serverul își dovedește identitatea la prima conexiune; avertismentul apare exact când amprenta nu se potrivește cu cea cunoscută (secțiunea 15.1). Ignorat, avertismentul deschide ușa unui server fals care îți captează tot ce trimiți — inclusiv cheile și secretele. Corect: verifici amprenta printr-un canal de încredere, apoi o accepți conștient.
3. „…rulează tot cu sudo27…” sudo ridică privilegiul unei comenzi la root — identitatea care ocolește majoritatea verificărilor (secțiunea 17.3). „Tot cu sudo” înseamnă că orice greșeală de logică sau orice document ostil7 acționează asupra întregului sistem. Corect: fiecare operație primește exact privilegiul de care are nevoie, justificat separat.
4. „…pune folderul curent la începutul PATH15-ului…” PATH-ul este lista de foldere în care shell-ul8 caută comenzi (secțiunea 5.1); el caută în ordine și se oprește la prima potrivire. Un folder la începutul listei îți suprascrie comenzile de sistem: un fișier numit apt în folderul curent devine comanda pe care o rulează agentul1. Corect: folderele de încredere stau în față, iar folderul curent nu intră deloc în PATH.
5. „…exportă toate cheile API5 într-o singură variabilă…” Variabila de mediu este valoare transmisă proceselor-copil (secțiunea 10). O singură variabilă cu toate cheile înseamnă că orice proces copil — și orice scurgere de date — expune tot setul deodată, iar o actualizare atinge toate serviciile odată. Corect: câte o variabilă separată per serviciu22, cu dreptul minim (secțiunea 17.1).
6. „…apoi execută apt update ca să actualizezi toate programele.” apt update actualizează numai indexul pachetelor — lista de ce există și de unde se descarcă (secțiunea 11.1). Instalarea și înnoirea efectivă sunt apt upgrade, iar pachetele de limbaj (pip, npm) nu sunt gestionate de apt28 deloc. Corect: actualizezi indexul, apoi upgrade-ul, conștient de fiecare strat.
7. „Dă systemctl enable agent.service și serviciul22 va porni imediat.” enable configurează pornirea automată la boot; start pornește serviciul acum (secțiunea 13.1). Sunt două operații diferite, care se pot face în orice ordine — dar una fără alta lasă fie un serviciu care nu pornește niciodată singur, fie unul pornit odată și dispărut la repornire.
8. „Dacă nu vezi nimic în terminal9, înseamnă că a mers.” Tăcerea terminalului nu este un cod de ieșire14. Starea4 reală se citește din $? — codul de ieșire al ultimei comenzi (secțiunea 9) — și din jurnalele serviciului cu journalctl (secțiunea 14). Un serviciu22 poate rula în fundal, poate eșua imediat sau poate aștepta — doar starea și codul de ieșire spun care variantă este reală.
9. „Pentru depanare, salvează întregul mediu al procesului și toate prompturile în log.” Mediul procesului conține cheile API5, iar prompturile pot conține date confidențiale ale utilizatorilor; un fișier de log cu tot acest conținut devine o copie a secretelor, citibilă oricui are acces la loguri (secțiunea 17.1). Corect: logurile conțin evenimente, coduri de ieșire14 și valori secretizate, nu secretele propriu-zise.
Concluzie. „Tutorialul” vinde viteza prin eliminarea fiecărei verificări care te-ar încetini o secundă — și construiește exact agenții1 care cad cel mai greu de depanat: identitate neconfirmată, privilegii maxime, PATH15 slăbit, secrete împrăștiate, stări interpretate după tăcere. Lecția arată contrariul: fiecare mecanism (SSH25, sudo27, PATH, variabile de mediu17, apt28, systemd21, coduri de ieșire14, loguri) are un rol precis, iar verificările care par să încetinească sunt cele care fac sistemul reparabil, nu doar pornibil.

21. Aceleași concepte în cinci tipuri de agenți
Linux și shell-ul8 nu sunt „subiecte DevOps” separate de agent1. Ele apar oriunde agentul atinge fișiere, procese sau servicii22.
Agent de programare
Rulează teste, compilări și verificatoare automate de cod (linters). Are nevoie de PATH15 reproductibil, ghilimele folosite corect pentru căi, coduri de ieșire14 și separare între stdout11 și stderr12.
Agent pentru documente
Procesează foldere mari de fișiere. Are nevoie de căi clare, expansiune18 controlată a numelor de fișiere (globbing19), lanțuri de comenzi (pipeline-uri29) care nu ascund erori și loguri care nu publică documentele-sursă.
Agent de voce
Poate rula ca serviciu22 permanent. systemd21, mediul de execuție, politica de repornire și journalctl24 devin mai importante decât shell-ul8 interactiv în care l-ai testat prima dată.
Agent de infrastructură
Folosește SSH25 și comenzi la distanță. Verificarea cheii de gazdă, conturile de serviciu22 și privilegiul minim devin parte din proiectarea instrumentelor3.
Agent pentru media sau ML local
Depinde de executabile, modele și lanțuri de instrumente3 (toolchains) care trebuie găsite prin PATH15 și instalate în stratul potrivit. Un mediu diferit poate schimba complet runtime-ul care ajunge să fie folosit.
22. Fișa unui spațiu de lucru reproductibil de linie de comandă
În laborator vei construi un spațiu de lucru reproductibil: un proiect pe care altcineva îl poate recrea și înțelege. Înainte de asta, stabilim ce înseamnă „reproductibil” la acest nivel.
Întrebarea: Unde este rădăcina proiectului?
De ce contează: Rădăcina este punctul de referință al tuturor căilor relative: dacă ea se mișcă, config-urile, datele și scripturile „dispar” fără să se fi schimbat nimic în ele.
Cum afli răspunsul: Din comanda de pornire a proiectului și din pwd rulat în folderul din care lucrezi — calea afișată este rădăcina efectivă, nu cea pe care o presupui.
Un răspuns bun și unul slab: Bun: „o cale clară, de pildă ~/ai-agents-zero-to-expert.” Slab: „folderul în care eram când a mers” — o rădăcină pe care nimeni nu o poate recrea mâine.
Întrebarea: Ce shell8 presupun exemplele?
De ce contează: Comenzi identice se comportă diferit între shell-uri8: sintaxa variabilelor, expansiunile și opțiunile interne diferă între Bash și alte shell-uri.
Cum afli răspunsul: Din echo $0 rulat în sesiunea în care vei folosi exemplele — afișează shell-ul8 care le va interpreta.
Un răspuns bun și unul slab: Bun: „Bash, pe Ubuntu 24.04.” Slab: „orice shell8” — sintaxa din exemple nu este portabilă fără specificare.
Întrebarea: Ce comenzi externe sunt necesare?
De ce contează: Fiecare comandă externă este o dependență: dacă nu există sau există altfel, scriptul tău se rupe în punctul acela, la orice rulare nouă.
Cum afli răspunsul: command -v nume-comandă confirmă prezența, iar nume-comandă --version arată versiunea — ambele rulate pe mașina țintă, nu pe a ta.
Un răspuns bun și unul slab: Bun: „jq, verificat cu command -v jq și jq --version.” Slab: „folosim jq” — fără verificare, dependența nu poate fi confirmată de altcineva.
Întrebarea: Ce vine din APT28?
De ce contează: Pachetele de sistem instalate prin APT28 (secțiunea 11.1) trebuie diferențiate de pachetele limbajului: se instalează altfel, se actualizează altfel și se pierd altfel la reconstruire.
Cum afli răspunsul: Din lista de instalare a proiectului: sudo apt install … cu numele exacte, iar ce este deja instalat se confirmă cu dpkg -l | grep nume.
Un răspuns bun și unul slab: Bun: „graphviz și acl, instalate prin APT28.” Slab: „diverse unelte de sistem” — o listă pe care nimeni nu o poate recrea.
Întrebarea: Ce variabile de mediu17 sunt obligatorii?
De ce contează: Variabilele lipsesc sau diferă între contexte — exact greșeala din secțiunea 19.1 —, iar numele lor trebuie scrise fără secretele propriu-zise, care stau în alt loc.
Cum afli răspunsul: Din program: fiecare citire de mediu (de pildă API_URL) este o variabilă obligatorie; numele intră în documentație, valorile în variabilele de sesiune sau într-un fișier local ignorat de Git.
Un răspuns bun și unul slab: Bun: „API_URL — din .env local, ignorat de Git; fără valori scrise în documentație.” Slab: „API5_URL=cheia-mea-secretă” — un secret publicat în fișa de proiect.
Întrebarea: Ce CWD16 presupune programul?
De ce contează: Căile relative se interpretează din CWD16 (secțiunea 4.2) — exact greșeala „merge în terminal9, eșuează ca serviciu22” din secțiunea 16.
Cum afli răspunsul: Din comenzile programului: dacă citește configs/app.json fără cale absolută, CWD-ul16 este o dependență; ideal, o elimini ancorând căile la rădăcina proiectului calculată în cod.
Un răspuns bun și unul slab: Bun: „rulabil din orice folder; căile sunt ancorate la rădăcina proiectului în cod.” Slab: „trebuie rulat din folderul X, dar nu scriem asta nicăieri” — o dependență ascunsă.
Întrebarea: Unde merg stdout11 și stderr12?
De ce contează: Cele două fluxuri sunt separate (secțiunea 7); dacă nu spui unde merg, sistemul decide pentru tine — iar un serviciu22 pornit de systemd21 le trimite implicit în jurnal23.
Cum afli răspunsul: Din modul de pornire: terminal9 — apare direct; fișier — redirecționare > și 2>; serviciu22 — journald (secțiunea 14), citibil cu journalctl -u nume.
Un răspuns bun și unul slab: Bun: „stdout11 în fișierul de rulare, stderr12 în terminal9; ca serviciu22, ambele în journal.” Slab: „se văd în terminal” — fals pentru orice proces pornit de altcineva sau programat.
Întrebarea: Cum este detectat eșecul?
De ce contează: Tăcerea nu dovedește succesul (secțiunea 9): doar codul de ieșire spune sistemului cum s-a terminat procesul, iar regulile pentru coduri trebuie să fie clare.
Cum afli răspunsul: Din program: ce cod întoarce la succes, ce la eșec și cum le citește cine le apelează — echo $? după rulare arată codul real.
Un răspuns bun și unul slab: Bun: „0 = succes; 1 = date invalide; 2 = lipsă configurare; apelantul verifică $?.” Slab: „programul plânge dacă nu merge” — mesajele nu sunt coduri.
Întrebarea: Rulează manual sau ca serviciu22?
De ce contează: Modul de rulare decide cine pornește procesul, cu ce mediu și ce se întâmplă la reboot — diferența din secțiunea 13 dintre pornirea manuală și serviciu22 administrat.
Cum afli răspunsul: Din frecvența și durata sarcinii: rulări ocazionale — manual; proces permanent sau programat — serviciu22, cu fișier unit și comenzi systemctl.
Un răspuns bun și unul slab: Bun: „manual în faza de test; ca serviciu22 agent.service după stabilizare.” Slab: „depinde” — o dependență de rulare care nu poate fi documentată.
Întrebarea: Rulează local sau la distanță?
De ce contează: La distanță, execuția se mută pe alt sistem (secțiunea 15): alt PATH15, alt CWD16, alte reguli — iar autentificarea trebuie făcută fără publicarea secretelor.
Cum afli răspunsul: Din locul unde trebuie să ruleze modelul și datele; accesul se face prin SSH25 cu chei, iar cheile private rămân în afara proiectului și a documentației.
Un răspuns bun și unul slab: Bun: „local pe laptop; la distanță pe server, cu cheie SSH25 dedicată agentului1, cheia privată în afara proiectului.” Slab: „ne conectăm cu parola” — o parolă tastată de fiecare dată este o invitație la automatizare fragilă.
Întrebarea: Unde verifici logurile?
De ce contează: Depanarea (secțiunea 14) începe din loguri: dacă nu știi unde sunt, incidentul devine ghicire — și logurile care conțin secrete devin ele însele un incident.
Cum afli răspunsul: Din modul de rulare: serviciu22 — journalctl -u nume; rulare manuală cu redirecționare — fișierul în care au fost scrise stdout11 și stderr12.
Un răspuns bun și unul slab: Bun: „journalctl -u agent.service pentru serviciu22; logs/ ignorat de Git pentru rulările manuale.” Slab: „în terminal9, undeva” — adică nicăieri.
Întrebarea: Ce fișiere sunt sursă și ce fișiere sunt generate?
De ce contează: Sursele se editează și intră în Git; generatele se produc și de obicei se ignoră — amestecarea lor duce la versionarea rezultatelor și la pierderea surselor (pregătește lecția despre Git).
Cum afli răspunsul: Pentru fiecare fișier te întrebi: îl scriu eu sau îl produce o comandă? Sursa intră în Git, generatul primește regulă în .gitignore.
Un răspuns bun și unul slab: Bun: „surse: catalogul JSON și scripturile; generate: diagrama PNG și inventarul.” Slab: „toate sunt importante, le ținem pe toate” — fără separare, istoricul Git devine neutilizabil.
Reproductibil nu înseamnă neapărat „identic bit cu bit pe orice computer”. Înseamnă că dependențele, contextul și pașii esențiali sunt expliciți suficient de bine încât altcineva să poată recrea mediul și să știe unde să caute dacă rezultatul diferă.
23. Exerciții practice
Exercițiile verifică modelul mental înainte de laboratorul în care vei construi spațiul de lucru complet.
Exercițiul 1 — Terminal sau shell?
Pentru fiecare situație spune ce componentă investighezi prima: fontul nu afișează corect caractere; cd are comportament neașteptat; un proces nu are drept de scriere; $VAR nu se expandează.
Răspuns
Fontul și randarea aparțin aplicației de terminal9; cd și expansiunea18 $VAR aparțin shell-ului8; dreptul de scriere aparține procesului și permisiunilor sistemului de fișiere. Terminalul este aplicația care desenează textul și trimite tastele, shell-ul este programul care interpretează comenzi (secțiunea 1), iar procesul este execuția vie, cu identitate și drepturi proprii (Lecția 5, secțiunea 4).
Explicațiile, în ordine: caracterele prost randate sunt un lucru pe care îl desenează fereastra — problema e în font sau în randarea emulatorului. cd este o comandă internă a shell-ului8, iar $VAR este expansiune18 făcută de shell înainte să pornească orice program (secțiunea 6) — ambele aparțin shell-ului. Dreptul de scriere este verificat de kernel pentru procesul care cere operația, cu credențialele lui — deci proces, plus sistemul de fișiere.
Capcana: toate patru „se văd în terminal9”, dar trei componente diferite le produc. Pentru constructorul de agenți1, această triere este primul pas de depanare: fiecare componentă are alte comenzi de verificare — font în emulator, cd și echo $VAR în shell8, ls -l și ps pentru proces și permisiuni.
Exercițiul 2 — Cale relativă sau absolută?
Programul deschide configs/app.json și funcționează numai când este pornit din rădăcina proiectului. Ce dependență ascunsă are?
Răspuns
Programul depinde de folderul curent de lucru (CWD16): calea relativă configs/app.json este interpretată pornind din folderul din care a fost pornit procesul. CWD (current working directory) este parte din starea4 procesului (secțiunea 4.2), iar o cale relativă pornește de la el, nu de la rădăcina sistemului.
Exemplu concret: același script pornit din rădăcina proiectului deschide configs/app.json cu succes; pornit din /tmp, încearcă /tmp/configs/app.json — un fișier care probabil nu există. Comanda pwd rulată în fiecare context arată cele două foldere diferite.
Capcana: „merge când pornesc de aici” este un simptom clasic al dependenței de CWD16, nu o dovadă de corectitudine. Pentru constructorul de agenți1, regula este: căile relative ale agentului se fac explicite sau se ancorează la rădăcina proiectului, iar contextul de execuție se verifică înainte de a suspecta codul.
Exercițiul 3 — De ce "$FILE"?
Explică de ce o variabilă care conține o singură cale ar trebui transmisă de regulă între ghilimele duble.
Răspuns
Valorile se transmit între ghilimele duble pentru că ele păstrează calea ca un singur argument și împiedică împărțirea în cuvinte și expansiunea18 numelor de fișiere — deși valoarea variabilei se înlocuiește în continuare. Expansiunea este înlocuirea $FILE cu valoarea lui de către shell8, înainte ca programul să pornească (secțiunea 6.1).
Exemplu concret: dacă FILE conține raport final.txt, comanda cp "$FILE" backup/ copiază un singur fișier cu numele complet; fără ghilimele, shell-ul8 livrează două argumente — „raport” și „final.txt” — și expandează orice * din nume în liste de fișiere.
Capcana: ghilimelele simple opresc și expansiunea18 — shell-ul8 ar transmite literal $FILE. Pentru constructorul de agenți1, ghilimelele duble în jurul fiecărei variabile sunt regula care separă „am șters raport final.txt” de „am șters raport și orice alt fișier care se potrivea cu tiparul”.
Exercițiul 4 — Unde merge eroarea?
Ai producer | consumer. producer scrie un avertisment pe stderr12. Ajunge avertismentul în stdin-ul10 lui consumer?
Răspuns
Nu prin operatorul | obișnuit: el conectează stdout-ul11 lui producer la stdin-ul10 lui consumer, iar stderr12 rămâne un flux separat, care apare direct în terminal9. Pipe-ul13 este canalul dintre două procese (secțiunea 8.2); stderr este fluxul 2, cel pentru erori, distinct de fluxul 1 (stdout).
Exemplu concret: grep error fisier.log | wc -l trimite rezultatele lui grep către wc -l; dacă grep scrie și un avertisment pe stderr12, avertismentul sare peste pipe13 și apare în terminal9 — de aceea nu este numărat de wc.
Capcana: să crezi că avertismentul „a dispărut” în pipe13 — el a luat alt descriptor. Pentru constructorul de agenți1, regula dă control fin asupra fluxurilor: dacă vrei erorile în analiza ta, le redirecționezi explicit cu 2>&1; dacă nu, le lași separate ca să le vezi în terminal9.
Exercițiul 5 — Variabilă locală sau variabilă de mediu?
De ce API_URL=value și export API_URL=value nu sunt echivalente pentru un program pornit după aceea?
Răspuns
API_URL=value creează sau modifică o variabilă a shell-ului8 curent, iar export API_URL=value o marchează și pentru includerea în mediul proceselor pornite ulterior de shell. Variabila de shell există doar în interiorul shell-ului; variabila de mediu este copiată în fiecare proces-copil la pornire (secțiunea 10).
Exemplu concret: fără export, programul pornit de shell8 după aceea primește un mediu în care API_URL nu există deloc — de aceea citirea ei în program întoarce gol. Cu export, copilul o primește.
Capcana: să crezi că mediul „se transmite înapoi” — el merge numai de la părinte la copil, niciodată invers (secțiunea 10.1). Pentru constructorul de agenți1, distincția explică de ce un export făcut în terminal9 nu ajunge niciodată la un agent pornit de systemd21 — serviciul22 are propriul mediu, definit în fișierul lui unit.
Exercițiul 6 — Enabled, dar inactive
Un serviciu22 apare enabled și inactive. Este contradicție?
Răspuns
Nu — starea4 enabled descrie configurarea pentru pornirea automată la boot, iar active/inactive descrie starea curentă a serviciului; cele două stări descriu lucruri diferite. enabled înseamnă că systemd21 a creat legătura care pornește serviciul22 la fiecare pornire a sistemului; active/inactive spun dacă procesul rulează acum (secțiunea 13.1).
Exemplu concret: un serviciu22 enabled și inactive este perfect legitim: va porni la următorul boot, dar nu a fost încă pornit manual. Comanda systemctl enable --now agent.service le combină: configurează pornirea automată și pornește serviciul imediat.
Capcana: exact confuzia din secțiunea 20 — „enable” citit ca „pornește acum”. Pentru constructorul de agenți1, distincția explică de ce agentul „nu pornește” după enable: comanda programează viitorul, iar pornirea imediată este o a doua comandă sau opțiunea --now.
Exercițiul 7 — Două chei, două întrebări
Explică diferența dintre intrarea din known_hosts și cheia publică din authorized_keys.
Răspuns
known_hosts îl ajută pe client să verifice identitatea serverului, iar authorized_keys îi spune serverului ce chei publice pot autentifica utilizatorul — două verificări în direcții opuse. known_hosts este fișierul clientului cu amprentele (host key) serverelor de încredere; authorized_keys este fișierul serverului cu cheile publice care pot deschide un cont (secțiunea 15).
Povestea, în ordine: serverul își prezintă cheia de gazdă26, clientul o compară cu known_hosts — dacă se potrivește, serverul e cel de la ultima conectare; apoi utilizatorul își dovedește identitatea cu cheia privată, iar serverul acceptă dacă cheia publică potrivită figurează în authorized_keys.
Capcana: să crezi că „serverul e verificat” înseamnă „sunt autorizat” sau invers. Pentru constructorul de agenți1, ambele fișiere țin securitatea conexiunii agentului: primul protejează agentul de un server fals, al doilea protejează serverul de agenți neinvitați.
Exercițiul 8 — Merge manual, eșuează la distanță
Un script rulează local, dar ssh host 'script' spune command not found. Enumeră trei ipoteze care nu presupun că scriptul este „stricat”.
Răspuns posibil
Trei ipoteze fără să atingi scriptul: PATH-ul15 sesiunii la distanță este diferit; mediul virtual nu este activ; comanda există numai local sau la o altă cale pe sistemul la distanță. PATH-ul este lista de foldere în care shell-ul8 caută comenzi (secțiunea 5.1), iar o sesiune SSH25 non-interactivă pornește cu un PATH adesea mai scurt decât sesiunea ta interactivă.
Exemplu concret: ssh host 'command -v python3' poate întoarce /usr/bin/python3 chiar dacă sesiunea ta interactivă pe același host folosește .venv/bin/python3 — aceeași comandă, alt rezultat, pentru că mediile diferă.
Capcana: să concluzionezi „scriptul e stricat” — el rulează, dar în alt context de execuție (secțiunea 16). Pentru constructorul de agenți1, exercițiul fixează lista de verificare a execuției la distanță: identitate, CWD16, PATH15 și mediu — fiecare inspectată cu command -v, pwd și echo $VAR rulate pe sistemul la distanță, nu local.
Exercițiul 9 — Prezice înainte să rulezi
Scrie pe hârtie ce afișează fiecare linie, apoi verifică în terminal9:
printf '%s\n' a b c | wc -l
ls fisier-inexistent 2> /dev/null; echo "$?"
X=1; bash -c 'echo "[$X]"'Răspuns
Cele trei linii afișează 3, apoi 2, apoi [] — și fiecare rezultat are un motiv distinct. Prima linie: printf '%s
' a b c | wc -l — printf scrie trei linii (a, b, c), iar wc -l le numără și afișează 3.
A doua linie: ls fisier-inexistent 2> /dev/null; echo "$?" — ls eșuează, dar erorile ei (descriptorul 2) sunt trimise în /dev/null, „coșul” care înghite tot ce primește; terminalul9 rămâne curat, iar echo "$?" afișează 2 — codul de ieșire al lui ls (secțiunea 9). Tăcerea nu înseamnă succes: codul o spune.
A treia linie: X=1; bash -c 'echo "[$X]"' — X=1 este variabilă de shell8, nu exportată, deci procesul-copil bash -c primește un mediu fără ea și afișează [] (secțiunea 10). Cu export X înainte, copilul ar fi afișat [1].
Capcana: să crezi că variabila „ajunge” la copil pentru că e vizibilă în shell8. Pentru constructorul de agenți1, exercițiul arată cele trei lecții deodată: numărarea prin pipe13, separarea erorilor de rezultate și transmiterea mediului numai către copil.
24. Quiz cu răspunsuri explicate
Răspunde înainte să deschizi explicația. Încearcă să justifici prin context, flux sau responsabilitate, nu doar prin memorarea comenzii.
Terminalul9 și Bash sunt același program?
Deschide răspunsul
Nu — terminalul9 este aplicația care desenează fereastra de text, iar Bash este shell-ul8 care rulează în interiorul ei și interpretează limbajul de comandă. Terminalul (emulatorul de terminal) este aplicația grafică: desenează literele, trimite tastele apăsate mai departe și nu înțelege nimic din ce scrii. Shell-ul este programul care primește textul, îl interpretează ca limbaj de comandă și pornește programe (secțiunea 1).
Povestea, în ordine: deschizi fereastra terminalului; terminalul9 pornește un shell8 în interior — pe Ubuntu, de regulă Bash; tu scrii „ls” în fereastră, terminalul transmite textul shell-ului, Bash îl interpretează, pornește programul ls și trimite rezultatul înapoi, iar fereastra doar îl desenează.
Cele două se pot schimba independent: poți rula același Bash într-un alt emulator de terminal9, sau poți pune alt shell8 (de pildă zsh) în aceeași fereastră. Iar Bash poate rula și fără nicio fereastră: serviciile pornite de systemd rulează shell-uri și scripturi fără niciun terminal atașat — exact situația din secțiunea 16, „merge în terminal, dar serviciul22 eșuează”.
Capcana: să configurezi „terminalul9” când de fapt configurezi shell-ul8, sau invers — variabilele de mediu, PATH-ul15 și promptul2 aparțin shell-ului, fereastra doar le arată. Pentru constructorul de agenți1, distincția explică de ce un agent sau un serviciu22 nu are terminal: terminalul este o comoditate pentru oameni, iar bucla agentului poate rula orbește, fără nicio interfață desenată.
Ce face pwd?
Deschide răspunsul
pwd (print working directory) afișează folderul curent de lucru al procesului shell8 — calea în care te afli acum. Folderul curent de lucru este parte din starea4 procesului (secțiunea 4.2): orice cale relativă tastată pornește de la ea, iar procesele pornite de shell o moștenesc.
Demonstrație reală pe Ubuntu 24.04, rulată în folderul proiectului:
pwd
/home/ana/ai-agents-zero-to-expertComanda nu primește argumente relevante — ea citește starea4 procesului propriu și scrie calea; fiecare parte a numelui afișat este un folder traversat.
Capcana: să confunzi folderul curent cu folderul unde se află programul rulat — un script pornit din altă parte rulează cu folderul curent al celui care l-a pornit, nu cu al lui. Pentru constructorul de agenți1, asta explică o familie întreagă de erori din secțiunea 16: același agent citește „config.json” cu succes în terminal9 și eșuează ca serviciu22, pentru că procesul de serviciu are un alt folder curent de lucru.
De ce ./tool și tool pot porni programe diferite?
Deschide răspunsul
./tool numește explicit programul din folderul curent, prin cale relativă, iar tool lasă shell-ul8 să caute comanda în folderele din PATH — două programe diferite pot răspunde la cele două comenzi. Calea relativă pornește din folderul curent (secțiunea 4), iar PATH-ul15 este lista ordonată de foldere în care shell-ul caută comenzi fără cale (secțiunea 5.1).
Demonstrație reală pe Ubuntu 24.04, în folderul /tmp/l6demo cu un program numit tool care scrie „din folderul curent” și altul instalat în /tmp/l6demo/bin/tool care scrie „versiunea din PATH15”:
PATH="/tmp/l6demo/bin:$PATH" tool
versiunea din PATH
./tool
din folderul curentAcelași nume tastat, două programe diferite: tool a rezolvat prin PATH15 spre /tmp/l6demo/bin/tool, iar ./tool a rulat fișierul din folderul curent.
Capcana: shell-ul8 caută intenționat numai în folderele din PATH15, nu și în folderul curent — o convenție de securitate, fiindcă un fișier ostil7 numit ls în folderul curent ar deveni comanda ta. Pentru constructorul de agenți1, regula arată de ce scripturile laboratorului se pornesc cu ./src/…: calea relativă spune explicit „rulează acest fișier de aici”, nu „caută un program cu numele ăsta undeva în sistem”.
Ce protejează ghilimelele duble în "$FILE"?
Deschide răspunsul
Ghilimelele duble păstrează rezultatul expansiunii $FILE ca un singur argument, chiar dacă conține spații — împiedică împărțirea în cuvinte și expansiunea18 numelor de fișiere, deși valoarea variabilei se înlocuiește în continuare. Expansiunea este înlocuirea $FILE cu valoarea lui de către shell8, înainte ca programul să pornească (secțiunea 6).
Exemplu concret: dacă FILE conține raport final.txt, comanda cp "$FILE" backup/ primește un singur argument cu spațiul lui — iar fără ghilimele shell-ul8 îl rupe în două argumente, „raport” și „final.txt”, și expandează orice * din nume în liste de fișiere.
Capcana: ghilimelele simple '…' opresc și expansiunea18 — shell-ul8 ar transmite literal $FILE, nu valoarea. Regula practică: ghilimele duble în jurul fiecărei variabile din comenzi. Pentru constructorul de agenți1, asta este diferența dintre un agent care șterge „raport final.txt” și unul care șterge din greșeală „raport” și „final.txt” — și orice alt fișier care se potrivește cu tiparul.
Ce descriptor este stdout11?
Deschide răspunsul
stdout11 este descriptorul 1 — fluxul standard prin care procesul scrie datele sale de ieșire. Descriptorii de fișier sunt numerele mici prin care procesul referă obiectele deschise (secțiunea 7): 0 este stdin10 (intrarea), 1 este stdout (ieșirea normală), 2 este stderr12 (ieșirea pentru erori).
Exemplu concret: comanda echo text scrie „text” pe descriptorul 1; redirecționarea echo text > fișier.txt mută acel flux 1 în fișier, iar 2> mută separat fluxul 2. De aceea poți separa rezultatele de erori: ele sunt două fluxuri diferite, cu numere diferite.
Capcana: să crezi că erorile apar „în același loc” cu rezultatele — în terminal9 ambele sunt desenate la fel, dar sunt fluxuri separate. Pentru constructorul de agenți1, distincția decide ce ajunge unde: rezultatele pe stdout11 pentru alte programe, erorile pe stderr12 pentru oameni și jurnale23 — amestecarea lor strică pipe13-urile și logurile.
A | B trimite stderr-ul12 lui A către B?
Deschide răspunsul
Nu — pipe-ul13 obișnuit conectează stdout-ul11 lui A la stdin-ul10 lui B; stderr-ul12 lui A nu trece prin el. Pipe-ul (secțiunea 8.2) este canalul dintre două procese: ce scrie A pe descriptorul 1 ajunge direct ca intrare (descriptorul 0) la B.
Exemplu concret: grep error fisier.log | wc -l trimite rezultatele lui grep către wc -l, care le numără; dacă grep scrie și o eroare pe stderr12, aceasta sare peste pipe13 și apare direct în terminal9 — de aceea erorile nu sunt „numărate” din greșeală.
Capcana: să crezi că erorile „au dispărut” când canalizezi ieșirea — ele doar au luat alt drum, cel al descriptorului 2. Pentru constructorul de agenți1, asta dă controlul fin: 2>&1 combină erorile cu rezultatele când vrei să le prindeți împreună, iar separate, lăsate în afara pipe13-ului, când vrei să le vezi separat.
Ce înseamnă, în mod convențional, codul de ieșire 0?
Deschide răspunsul
Codul de ieșire 0 înseamnă, prin convenție, succes; orice valoare nenulă semnalează un eșec sau o stare4 specială definită de program. Codul de ieșire este numărul pe care procesul îl întoarce sistemului la terminare (secțiunea 9) — este canalul prin care o comandă comunică rezultatul, fără text.
Exemplu concret: grep întoarce 0 când a găsit potriviri, 1 când nu a găsit și 2 la eroare — trei rezultate diferite, fără niciun cuvânt afișat. De aceea operatorii && și || funcționează: ei citesc codul, nu textul (secțiunea 9.1).
Capcana: să interpretezi „nu a scris nimic” ca „a reușit” — exact greșeala din secțiunea 20. Pentru constructorul de agenți1, regula decide cum îți verifici propriile comenzi: după $? și după codurile documentate, nu după lipsa mesajelor de eroare.
De ce este util &&?
Deschide răspunsul
&& exprimă o dependență: comanda din dreapta rulează numai dacă cea din stânga a reușit — adică numai dacă codul ei de ieșire a fost 0. Operatorul citește codul de ieșire al comenzii din stânga (secțiunea 9.1) și decide dacă lanțul continuă; perechea lui, ||, rulează dreapta numai la eșec.
Exemplu concret: apt update && apt upgrade — actualizarea indexului rulează întâi, iar upgrade-ul pornește numai dacă indexul s-a actualizat cu succes; dacă primul pas eșuează, al doilea nu mai pornește deloc.
Capcana: ; separă comenzi care rulează întotdeauna, una după alta, indiferent de rezultat — de aceea „comandă 1; comandă 2” și „comandă 1 && comandă 2” nu sunt echivalente. Pentru constructorul de agenți1, && este forma cea mai simplă a dependenței pe care lecția o generalizează: pasul următor pornește numai dacă precedentul a reușit.
De ce folosim source .venv/bin/activate?
Deschide răspunsul
source .venv/bin/activate rulează scriptul în interiorul shell-ului8 curent, astfel încât el să modifice mediul acestuia — în special variabila PATH, care primește folderul .venv/bin în față. source (sau .) nu pornește un proces separat: el citește fișierul și îl execută în shell-ul existent (secțiunea 10.1).
Exemplu concret: după activare, which python3 afișează .venv/bin/python3 — interpretul mediului virtual —, iar pip instalează în mediul proiectului, nu în sistem. Un proces separat (de pildă bash activate) ar modifica mediul propriu și s-ar termina, lăsând shell-ul8 tău neatins: exact de ce activarea trebuie făcută cu source, nu prin execuție directă.
Capcana: să crezi că mediul „se transmite înapoi” către shell-ul8 părinte — el se transmite numai de la părinte către copil (secțiunea 10.1). Pentru constructorul de agenți1, asta explică de ce activarea mediului nu „strică” procesul agentului și de ce un serviciu22 systemd21 își construiește mediul prin fișierul unit, nu prin activare în terminal9.
Ce face apt update?
Deschide răspunsul
apt update actualizează indexul local al pachetelor disponibile, pornind de la sursele de pachete configurate — și nu actualizează pachetele instalate. Indexul este lista locală „ce pachete există, în ce versiuni, de unde se descarcă”; sursele sunt adresele de unde se citește lista (secțiunea 11.1).
Exemplu concret: după apt update, apt list --upgradable arată ce pachete au versiuni noi; instalarea lor efectivă se face cu apt upgrade — o comandă separată, care descarcă și înnoiește pachetele propriu-zise.
Capcana: să rulezi doar apt update și să crezi că sistemul „e la zi” — ai actualizat doar lista, nu pachetele. Pentru constructorul de agenți1, separarea celor două comenzi este un model: o comandă citește starea4 lumii externe, alta aplică schimbări — exact distincția dintre observare și acțiune din Lecția 2.
De ce este apt-get preferabil lui apt în scripturi?
Deschide răspunsul
apt-get este preferabil în scripturi pentru că interfața lui este gândită pentru programe: comportamentul și afișajul lui rămân stabile între versiuni, pe când apt este gândit pentru oameni. apt28 este comanda modernă, prietenoasă — cu progres animat și mesaje prietenoase — dar compatibilitatea ieșirii ei între versiuni nu este garantată; apt-get păstrează contractul său mai îngust, dar stabil (secțiunea 11.1).
Exemplu concret: un script care parsa ieșirea apt poate rupe la o versiune nouă a managerului, deși pachetele funcționează identic; același script pe apt-get rămâne valabil.
Capcana: să alegi comanda „mai nouă și mai frumoasă” pentru automatizare. Pentru constructorul de agenți1, regula este generală: instrumentele3 cu care interacționează un agent — nu un om — trebuie să aibă interfețe stabile, documentate și previzibile, iar cele prietenoase cu omul rămân pentru terminalul9 tău.
systemctl enable pornește serviciul22 imediat?
Deschide răspunsul
Nu — systemctl enable configurează doar pornirea automată la boot; serviciul22 nu pornește acum; dacă intenția ta este asta, folosești enable --now. enable creează legătura care pornește serviciul la fiecare pornire a sistemului; start îl pornește imediat, o singură dată (secțiunea 13.1).
Exemplu concret: systemctl enable agent.service lasă serviciul22 neatins acum, dar pornit la următorul boot; systemctl start agent.service îl pornește acum, dar dispare la reboot; systemctl enable --now agent.service face ambele.
Capcana: exact greșeala din secțiunea 20 — „enable” citit ca „pornește acum”. Pentru constructorul de agenți1, distincția decide ce vezi după comandă: dacă agentul „nu a pornit” după enable, nu e o eroare — e comportamentul corect al unei comenzi care programează viitorul, nu prezentul.
Ce poate colecta journald de la un serviciu22 systemd21?
Deschide răspunsul
journald poate colecta stdout11 și stderr12 ale unităților de serviciu22 systemd21, împreună cu metadate structurate despre fiecare înregistrare. journald este serviciul de jurnal23 din systemd (secțiunea 14): el primește ce scriu procesele pe descriptorii 1 și 2 și adaugă lângă fiecare înregistrare câmpuri structurate — dată, unitate, PID, cod de prioritate.
Exemplu concret: un agent1 servit de systemd21 care scrie „verificare terminată” pe stdout11 ajunge în jurnal23 cu ora, numele unității și PID-ul atașate — căutabil cu journalctl -u agent.service, fără fișiere de log gestionate manual.
Capcana: să crezi că jurnalul23 conține doar textul primit — metadatele structurate sunt ce îl fac căutabil și corelabil. Pentru constructorul de agenți1, asta înseamnă că agentul servit de systemd21 primește jurnal automat: nu trebuie să construiești logarea, ci să scrii în stdout11/stderr12 informații care merită găsite mai târziu.
Ce verifică known_hosts?
Deschide răspunsul
known_hosts verifică dacă serverul SSH25 este cel pe care clientul îl cunoaște deja, după cheia lui de gazdă — nu verifică dacă cheia utilizatorului este autorizată pe server. Cheia de gazdă26 (host key) este amprenta publică a serverului; known_hosts este fișierul în care clientul SSH păstrează amprentele serverelor de încredere (secțiunea 15.1).
Povestea celor două verificări, în ordine: la conectare, serverul își prezintă cheia de gazdă26, iar clientul o compară cu intrarea din known_hosts — asta dovedește serverul; apoi utilizatorul se autentifică cu cheia lui privată, iar serverul verifică dacă cheia publică potrivită e autorizată — asta dovedește utilizatorul. Două verificări diferite, în direcții diferite.
Capcana: să confunzi „serverul e cel bun” cu „sunt eu cel bun pentru el”. Pentru constructorul de agenți1, asta decide ce securizezi: known_hosts protejează agentul de un server fals, iar autorizarea cheii pe server protejează serverul de agenți neinvitați — ambele trebuie să fie corecte.
Care este primul lucru pe care îl compari când un program merge manual, dar nu ca serviciu22?
Deschide răspunsul
Primul lucru comparat este contextul de execuție: identitatea procesului, folderul curent, executabilul și PATH-ul15, variabilele de mediu, permisiunile — apoi logurile, care arată diferența concretă. Contextul de execuție este setul de condiții sub care rulează procesul: cine (UID/GID și grupuri), unde (CWD16), ce programe poate găsi (PATH), ce variabile primește (secțiunea 16).
Exemplu concret din lecție: agentul1 citește config.json cu succes în terminal9 și eșuează ca serviciu22 — pentru că serviciul are alt folder curent de lucru. Comanda pwd rulată în ambele contexte arată diferența imediat, iar journalctl arată exact ce a încercat procesul de serviciu să deschidă.
Capcana: să compari „comenzile” în loc de „contextele” — aceleași comenzi cu alte credențiale și căi dau alte rezultate. Pentru constructorul de agenți1, lista aceasta este o listă de verificare: fiecare element poate fi inspectat cu o comandă, iar diferența găsită acolo este cauza reală, nu ghicirea.
25. Recapitulare — de la text tastat la proces controlabil
Lecția nu este o listă de comenzi. Este un model al drumului dintre intenție și execuție.
Terminalul9 afișează; shell-ul8 interpretează
Nu atribui terminalului regulile de folosire a ghilimelelor, rezolvarea prin PATH15 sau variabilele shell-ului8.
Procesul are context
CWD16, UID/GID, PATH15, mediu și descriptorii schimbă comportamentul aceleiași aplicații.
Calea relativă depinde de CWD16
Un serviciu22 sau o sesiune SSH25 poate porni din alt loc decât terminalul9 tău.
Ghilimelele separă datele de sintaxă
Un argument trebuie să rămână un argument, nu să fie reinterpretat accidental de shell8.
stdin10/stdout11/stderr12 sunt fluxuri separate
Pipe-ul13 obișnuit transmite stdout11; redirecționările controlează descriptorii.
Codul de ieșire este semnalul programatic
Automatizarea trebuie să urmărească succesul, nu doar textul afișat.
Mediul se transmite explicit
export, serviciile și sesiunile la distanță determină ce variabile vede procesul.
Instalarea are straturi
APT28/dpkg administrează pachete de sistem; managerii de pachete ai ecosistemelor de limbaj au altă responsabilitate.
Start ≠ enable
Starea4 curentă a serviciului și activarea lui viitoare sunt axe separate.
Logurile sunt dovadă operațională
Folosește journalctl24 și filtre, dar nu transforma logurile în depozit de secrete.
SSH25 verifică două identități
Cheia de gazdă26 pentru server, autentificare separată pentru utilizator.
Reproductibilitatea începe cu context explicit
Alt utilizator trebuie să poată afla de unde pornește, ce dependențe are și unde caută eroarea.
Formula de o pagină: shell-ul8 interpretează textul într-un context. Contextul include CWD16, PATH15 și mediul procesului. Comenzile primesc stdin10 și argumente, produc stdout11/stderr12 și un cod de ieșire14. Pipe13-urile și redirecționările compun aceste fluxuri. APT28 instalează software de sistem, systemd21 administrează servicii22, journald păstrează evenimente, iar SSH25 mută execuția pe alt sistem. În toate cazurile, procesul și permisiunile din Lecția 5 rămân fundamentul.
set -e, în scrierea fișierelor de unitate systemd21 sau în configurarea serverului SSH25. Scopul Lecției 6 este modelul: context → interpretare → proces → fluxuri → cod de ieșire14 → serviciu22 → loguri. Lecțiile următoare adaugă detalii peste el, fără să îl înlocuiască.
Vocabular esențial
Recitește definițiile după parcurgerea lecției — acum fiecare termen are context. Sunt grupate în trei familii: termenii moșteniți din Lecțiile 1–4, componentele liniei de comandă și mecanismele de operare. Apasă pe numărul de lângă un termen ca să ajungi la explicația lui; butonul Înapoi al browserului te duce înapoi unde citeai.
Vocabular din Lecțiile 1–4
| Termen | Ce înseamnă |
|---|---|
| 1 Agent | Sistem care urmărește un obiectiv, păstrează stare și alege dinamic acțiunile următoare, până la o condiție de oprire. Procesele din această lecție sunt materiile prime din care se construiește. Detaliat în Lecția 1. |
| 2 Prompt | Instrucțiunea trimisă modelului. Logarea ei este utilă pentru reconstituirea deciziilor, dar promptul nu este log și nu trebuie să conțină secrete. Detaliat în Lecția 1. |
| 3 Instrument | Capacitate pe care agentul o poate apela: o comandă, un script, un API. În această lecție, instrumentele sunt comenzi de shell. Detaliat în Lecția 1. |
| 4 Stare | Informația păstrată despre sarcina curentă: CWD, variabile exportate, fișiere deschise. Secțiunea 4.2 arată că folderul curent face parte din starea procesului. Detaliat în Lecția 1. |
| 5 API | Interfața prin care un program cere date sau acțiuni altui program. Cheile API autentifică aceste cereri — și nu trebuie să ajungă în loguri. Detaliat în Lecția 1. |
| 6 Controller | Componenta care coordonează bucla agentului: menține starea, alege pașii, aplică permisiunile. În laboratoarele 2 și 5 controllerul a fost construit direct în Python. Detaliat în Lecția 2. |
| 7 Prompt injection | Situația în care datele citite de agent conțin text care încearcă să se comporte ca instrucțiune. Logarea completă a unui mediu ostil publicează exact textul acela. Detaliat în Lecția 2. |
Componentele liniei de comandă
| Termen | Ce înseamnă |
|---|---|
| 8 Shell | Programul care interpretează limbajul de comandă: citește textul, îl expandează și pornește programele. Ca traducătorul dintre tine și sistem: tu scrii, el traduce în acțiuni ale sistemului. Shell-ul nu este fereastra în care scrii — aceea este terminalul. |
| 9 Terminal | Aplicația grafică care desenează fereastra de text și trimite tastele apăsate către shell. Ca ecranul și tastatura unui bancomat: transportă mesajele, nu le interpretează. Terminalul nu decide nimic — toate deciziile sunt ale shell-ului și ale programelor pornite. |
| 10 Stdin | Fluxul standard de intrare al unui proces — descriptorul 0: de aici primesc datele. Ca țeava de intrare a unui filtru de apă. stdin-ul nu este tastatura neapărat — poate fi ieșirea altei comenzi sau un fișier. |
| 11 Stdout | Fluxul standard de ieșire — descriptorul 1: aici procesul scrie rezultatele. Ca tubul de ieșire al filtrului: apa limpede iese pe aici. stdout-ul nu este ecranul neapărat — poate fi redirecționat într-un fișier sau într-un pipe. |
| 12 Stderr | Fluxul standard pentru erori — descriptorul 2: separat de stdout exact pentru ca erorile să nu se amestece cu rezultatele. Ca tubul roșu al filtrului: alte ape, alt drum. stderr-ul nu este doar „mesaje pentru oameni” — alte programe îl pot citi la fel. |
| 13 Pipe | Canalul care conectează stdout-ul unei comenzi la stdin-ul alteia: primul scrie, al doilea citește, simultan. Ca o țeavă între două filtre: apa curge în lanț. Pipe-ul nu transportă stderr — acela are propriul drum, dacă îl redirecționezi explicit. |
| 14 Cod de ieșire | Numărul pe care procesul îl întoarce sistemului la terminare: 0 prin convenție înseamnă succes, nenul înseamnă eșec sau stare specială. Ca bonul unui aparat: spune dacă tranzacția a trecut. Codul de ieșire nu este textul afișat — tăcerea nu înseamnă succes. |
| 15 PATH | Lista ordonată de foldere în care shell-ul caută comenzi fără cale. Ca agenda de numere a unui interfon: numele caută în ordine, iar primul găsit câștigă. PATH-ul nu include folderul curent — tocmai de aceea scripturile se pornesc cu ./. |
Operare: pachete, servicii, loguri și acces la distanță
| Termen | Ce înseamnă |
|---|---|
| 16 CWD | Current working directory: folderul din care procesul interpretează căile relative. Ca locul în care stai când dai indicații: „a doua ușă pe stânga” înseamnă altceva din fiecare cameră. CWD-ul nu este rădăcina proiectului — e doar de unde pornește procesul acum. |
| 17 Variabilă de mediu | Valoare numită care se copiază din shell în fiecare proces-copil la pornire — de pildă PATH sau API_URL. Ca rucsacul primit la plecare: pleacă de la părinte la copil, niciodată înapoi. Variabilele shell neexportate nu ajung în mediu. |
| 18 Expansiune | Înlocuirea făcută de shell înainte ca programul să pornească: variabile devin valori, tipare devin liste de fișiere. Ca traducerea textului înainte de trimitere: programul nu vede niciodată $FILE, ci rezultatul expansiunii. Expansiunea nu este magie de afișare — se face complet, înainte de execuție. |
| 19 Globbing | Expansiunea numelor de fișiere: shell-ul transformă tipare precum *.txt în lista fișierelor care se potrivesc. Ca secretarul care desface un plic adresat „tuturor din etajul 2” în plicuri individuale. Globbing-ul nu este filtrul programului — programul primește deja lista desfăcută. |
| 20 Substituție de comandă | Mecanismul prin care ieșirea unei comenzi devine parte din linia altei comenzi: $(comandă). Ca notița pe care o scrii cu rezultatul unei întrebări și o lipești în următoarea cerere. Substituția nu este pipe — una compune text, cealaltă conectează fluxuri. |
| 21 Systemd | Managerul de servicii și alte unități din Ubuntu: pornește procese la boot, le supraveghează și le ține jurnalul. Ca administrația blocului care aprinde scările și ține registrul de service. systemd nu este shell — el gestionează procese, nu interpretează comenzi. |
| 22 Serviciu | Proces administrat de systemd printr-un fișier unit: pornire la boot, repornire la eșec, jurnal centralizat. Ca un chelner cu ture programate: nu depinde de cineva care stă în terminal. Un serviciu nu are terminal — și tocmai de aceea contextul lui de execuție trebuie definit explicit. |
| 23 Jurnal | Înregistrarea a ceea ce a scris procesul: evenimente, erori, stări. Ca fișa medicală: reconstituie ce s-a întâmplat și când. Jurnalul nu este un seif — nu trebuie să conțină chei API sau date personale. |
| 24 Journalctl | Comanda prin care interoghezi jurnalul systemd: filtrezi după unitate, timp sau text. Ca motorul de căutare al fișei medicale: cauți pe pacient, pe dată, pe simptom. journalctl nu modifică nimic — doar citește. |
| 25 SSH | Secure Shell: protocolul prin care execuți comenzi pe alt sistem, cu autentificare și criptare. Ca linia telefonică criptată către alt birou: vorbirea e securizată, dar regulile biroului rămân ale lui. SSH-ul nu transferă automat mediul tău local — la distanță, PATH și CWD sunt ale sistemului de acolo. |
| 26 Cheie de gazdă | Amprenta publică prin care serverul SSH își dovedește identitatea la conectare. Ca holograma unui card bancar: confirmă că instituția e reală. Cheia de gazdă nu este cheia ta privată — una dovedește serverul, alta te dovedește pe tine. |
| 27 Sudo | Mecanismul care poate porni o comandă sub altă identitate — de obicei root — conform unei politici scrise. Ca portarul care verifică lista înainte să deschidă o ușă. sudo nu este „permisiunea root” și nu face automat o acțiune corectă. |
| 28 Apt | Managerul de pachete Debian/Ubuntu: instalează, actualizează și scoate software-ul de sistem, cu rezolvarea dependențelor. Ca piața centrală a pachetelor: listează ce există și de unde vine. apt update nu este apt upgrade — primul actualizează lista, al doilea pachetele. |
| 29 Pipeline | Lanțul de comenzi conectate prin pipe-uri, în care ieșirea fiecăreia devine intrarea următoarei. Ca linia de montaj: produsul trece prin toate posturile. Fără pipefail, starea raportată este cea a ultimei comenzi — nu a întregului lanț. |
| 30 Monitorizare | Urmărirea continuă a stării și a comportamentului unui sistem: jurnale, metrici, alerte. Ca camerele de supraveghere și senzorii unui bloc: înregistrează ce se întâmplă, nu doar ce se pretinde. Monitorizarea nu este același lucru cu securitatea — o ajută, dar nu înlocuiește permisiunile. |
26. Referințe și verificare tehnică
Afirmațiile tehnice din această lecție au fost verificate în documentația oficială și în manualele listate mai jos, cu o reverificare în octombrie 2026. Pentru mecanismele care produc frecvent confuzii am comparat documentația proiectului cu documentația Ubuntu sau cu standardul POSIX, după caz.
- GNU Bash Reference Manual — lanțuri de comenzi (pipeline-uri29), redirecționări, ghilimele, mediu, PATH15 și coduri de ieșire14.
- Ubuntu Server documentation — Package management — APT28,
apt,apt-getșidpkg. - systemctl(1) — documentația Ubuntu și systemd21: start, enable, --now, restart, reload și daemon-reload.
- journald.conf(5) — documentația Ubuntu și systemd21: Storage=, jurnal23 persistent/volatil și locațiile de stocare.
- Ubuntu manpages — journalctl(1) — interogarea jurnalului23 systemd21.
- OpenBSD / OpenSSH manual — ssh(1) — autentificare, chei de gazdă26,
known_hostsșiauthorized_keys. - Ubuntu Server documentation — OpenSSH server — configurare și depanare SSH25 pe Ubuntu.
- Dennis M. Ritchie, The UNIX Time-sharing System — A Retrospective — shell8, redirecționare și pipe-uri13 în designul Unix.
- POSIX.1-2024 — Shell Command Language — tokenizare, expansiuni18, redirecționări, pipeline-uri29, mediu și căutarea comenzilor.
- Ubuntu 24.04 LTS release notes — formatul structurat deb822 și fișierul ubuntu.sources.
27. Ce urmează
Lecția 6 ți-a dat limbajul operațional al sistemului: poți descrie unde rulează o comandă, cum ajung datele în proces, cum găsește executabilul, cum este transmis mediul, cum este administrat un serviciu22 și unde îi citești logurile.
Lecția 7 — Python, de la zero la util va muta aceeași disciplină într-un limbaj de programare: tipuri, funcții, clase, module, excepții, medii și pachete. Când vei scrie primul instrument3 tipizat, vei înțelege deja de unde vine procesul Python, ce executabil rulează, ce mediu primește și cum îi interpretezi codul de ieșire.