A la lliçó anterior vas aprendre a moure't per srv-veloz-01 i va quedar una pregunta a l'aire: per què cd no pot ser un programa extern. Aquesta pregunta és la porta d'entrada d'aquesta lliçó. Fins ara has fet servir el shell; ara entendràs què passa exactament entre que prems Enter i apareix el resultat. Aquest coneixement no és teòric: explica per què una variable definida en una canonada desapareix, per què which de vegades menteix, per què el teu script «no troba» una ordre que tu sí que veus, i per què source i executar un fitxer no són el mateix. Són precisament els errors que més temps fan perdre a qui programa en Bash sense aquest model mental.
Contingut
- Què passa quan prems Enter
- Els tipus d'ordre i com distingir-los
- El
PATHi l'ordre de cerca - Processos pare i fill:
forkiexec - Subshells i per què les variables no sobreviuen
- Entorn davant de variables de shell
$?: el codi de sortida- Posant-ho tot junt a Veloz Envíos
- Què passa quan prems Enter
Escrius ls -lh /var/log/veloz i prems Enter. El que sembla instantani són en realitat sis fases ben definides.
graph TD
A["Prems Enter"] --> B["1. Lectura i trossejat<br/>divideix la línia en paraules"]
B --> C["2. Expansions<br/>~, $VAR, *, $(...), aritmètica"]
C --> D["3. Resolució de l'ordre<br/>àlies → keyword → funció → builtin → PATH"]
D --> E{"És interna?"}
E -->|"Sí: builtin/funció"| F["Executa dins del mateix Bash"]
E -->|"No: executable extern"| G["4. fork(): crea procés fill"]
G --> H["5. exec(): el fill es converteix<br/>en el programa"]
H --> I["Bash espera amb wait()"]
F --> J["6. Codi de sortida a $?"]
I --> J
J --> K["Nou prompt"]
Vegem cada fase.
Fase 1: lectura i trossejat
Bash llegeix la línia completa i la divideix en paraules fent servir espais, tabuladors i salts de línia com a separadors. També identifica els operadors especials (|, >, &&, ;). De ls -lh /var/log/veloz en surten tres paraules.
Aquí ja apareix la primera font d'errors. Si un nom de fitxer conté espais, el trossejat el parteix en dos:
Bash hi entén tres arguments, no pas un. Per això les cometes són importants: ls "el meu informe.txt". Tot el detall és a 03-06.
Fase 2: expansions
Bash reescriu la línia abans d'executar res. Hi substitueix:
| Expansió | Exemple | Es converteix en |
|---|---|---|
| Claus | log-{a,b}.txt |
log-a.txt log-b.txt |
| Titlla | ~/veloz-ops |
/home/joan/veloz-ops |
| Paràmetres | $HOME |
/home/joan |
| Ordres | $(date +%F) |
2026-08-03 |
| Aritmètica | $((2 + 3)) |
5 |
| Rutes (globbing) | *.log |
acces.log app.log |
Aquest punt és crucial i sorprèn molta gent: l'ordre no veu mai els asteriscs. Quan escrius ls *.log, qui expandeix el patró és Bash, i ls rep ja la llista de noms. Ho pots comprovar:
echo no sap res de comodins: va rebre tres arguments ja resolts. El detall complet de les expansions es tracta a 02-05 i 03-06; aquí n'hi ha prou que sàpigues que passen abans.
Fase 3: resolució de l'ordre
Bash pren la primera paraula i cerca en un ordre estricte què és. Ho veurem a la secció 2, perquè és on es concentren les sorpreses.
Fases 4 i 5: fork i exec
Si l'ordre resulta ser un executable extern, Bash no es pot «convertir» en ell (deixaria d'existir el teu shell). Fa dues coses:
fork(): crea una còpia de si mateix, un procés fill.exec(): el fill reemplaça la seva pròpia imatge en memòria per la del programa que s'ha d'executar.
El pare (el teu Bash) es queda esperant amb wait() que el fill acabi. Per això el prompt no torna fins que l'ordre acaba, tret que la llancis en segon pla amb &.
Fase 6: codi de sortida
Quan el fill mor, retorna un número entre 0 i 255. Bash el desa a $?. És la base de tot el control d'errors en scripting, i el veurem a la secció 7.
- Els tipus d'ordre i com distingir-los
No tot el que escrius al prompt és un programa. Bash reconeix cinc categories, i les cerca en aquest ordre exacte:
| Ordre | Tipus | Què és | Exemples |
|---|---|---|---|
| 1 | Àlies | Substitució de text definida per tu | ll, ops |
| 2 | Keyword | Paraula reservada de la sintaxi de Bash | if, for, while, function, [[ |
| 3 | Funció | Bloc de codi que has definit | Les teves funcions de lib/ |
| 4 | Builtin | Ordre integrada en el mateix Bash | cd, echo, export, source, type |
| 5 | Executable extern | Un fitxer trobat al PATH |
ls, grep, awk, date |
L'ordre importa. Si defineixes una funció anomenada ls, es farà servir en lloc de /usr/bin/ls. I si a més defineixes un àlies ls, l'àlies guanya a la funció.
2.1 type: l'eina fiable
type és un builtin de Bash i respon exactament el que Bash faria.
cd és una ordre interna del shell ls és un àlies de «ls --color=auto» if és una paraula clau del shell echo és una ordre interna del shell
L'opció -a mostra totes les coincidències, per ordre de prioritat. És reveladora:
Existeixen dos echo: el builtin de Bash i el programa /usr/bin/echo de coreutils. Guanya el builtin, sempre. I no són idèntics: les seves opcions difereixen lleugerament entre sistemes, cosa que provoca scripts que funcionen en una màquina i no pas en una altra. Per això en scripting professional es prefereix printf, que és molt més predictible (ho veurem al Mòdul 3).
Un altre exemple molt il·lustratiu amb el nostre PATH de veloz-ops:
Hi apareixen dues rutes perquè als sistemes moderns /bin és un enllaç simbòlic a /usr/bin: és el mateix fitxer vist de dues maneres.
2.2 command -v: la versió per a scripts
command -v imprimeix la ruta (o la definició) i retorna un codi de sortida que indica si existeix o no. És la manera estàndard i portable de comprovar disponibilitat en un script:
if command -v jq > /dev/null 2>&1; then
echo "jq disponible"
else
echo "Falta jq: instal·la'l amb sudo apt install jq"
fiAquest patró el farem servir de debò al Mòdul 6, quan veloz-ops necessiti jq per parlar amb l'API.
2.3 which: per què no te'n pots refiar
Observa: which cd no imprimeix res. I aquí rau el problema. which és un programa extern (en molts sistemes, un script) que es limita a recórrer el PATH cercant fitxers. No sap res de builtins, funcions ni keywords, perquè no pot veure l'interior del teu shell.
| Eina | Tipus | Veu builtins | Veu funcions | Veu àlies | Portable |
|---|---|---|---|---|---|
type |
Builtin de Bash | Sí | Sí | Sí | Bash/ksh/zsh |
command -v |
Builtin POSIX | Sí | Sí | Sí | Sí, POSIX |
which |
Programa extern | No | No | Depèn | Inconsistent |
La conclusió pràctica: fes servir type -a per investigar de manera interactiva i command -v dins dels scripts. Evita which. Un cas real de confusió: un company es queixa que time no existeix perquè which time no retorna res; en realitat time és una keyword de Bash, i type time ho aclareix a l'instant.
- El
PATH i l'ordre de cerca
PATH i l'ordre de cercaQuan Bash arriba al pas 5 (executable extern), recorre els directoris de PATH d'esquerra a dreta i executa la primera coincidència.
Punts importants:
- La cerca s'atura a la primera coincidència. Si hi hagués un
lsa~/veloz-ops/bin, s'executaria aquell i no pas el del sistema. - L'ordre el decideixes tu. Posar el teu directori al davant dona prioritat a les teves eines; posar-lo al darrere és més segur.
- Bash desa a la memòria cau les rutes trobades per no repetir la cerca. Si instal·les un programa nou i Bash continua dient que no existeix, neteja la memòria cau:
O consulta què té memoritzat:
Aquest és un problema real i frustrant: acabes de moure un script de lloc, l'executes i Bash mira de llançar-lo des de la ruta antiga. hash -r ho resol en un segon.
3.1 Per què ./script.sh i no script.sh
Si ets a ~/veloz-ops/bin i escrius informe.sh, Bash cercarà al PATH. Com que el directori actual no és al PATH (i per seguretat no hi ha de ser, segons vam veure a 01-02), no el trobarà:
La solució és indicar una ruta explícita: ./informe.sh. Tan bon punt una paraula conté una barra /, Bash deixa de cercar al PATH i la tracta com a ruta directa.
Amb la nostra configuració de veloz-ops hi ha un matís agradable: com que vam afegir ~/veloz-ops/bin al PATH, els scripts que hi posem sí que es podran invocar pel seu nom des de qualsevol directori, igual que ls. És exactament l'efecte que buscàvem en muntar el toolkit.
- Processos pare i fill:
fork i exec
fork i execCada procés a Linux té un identificador (PID) i un pare (PPID). El teu shell també.
$$ és el PID del shell actual i $PPID el del seu pare (normalment l'emulador de terminal o sshd).
Comprovem la relació pare-fill en directe:
El fill té un PID diferent i el seu pare és el teu shell. Aquesta és la mecànica del fork.
Quan el fill acaba, tot el que va fer a la seva pròpia memòria desapareix: variables, directori actual, funcions. Només sobreviu allò que va escriure al disc o va enviar per la seva sortida estàndard. Aquest principi explica la meitat dels «misteris» de Bash.
4.1 L'excepció: exec sense fork
Si fas servir exec explícitament, Bash no crea cap fill: es reemplaça a si mateix pel programa.
Es mostra la data i la terminal es tanca, perquè el teu shell ja no existeix: es va convertir en date, i date va acabar. És una eina especialitzada, molt usada als entrypoint.sh de contenidors Docker perquè el procés de l'aplicació hereti el PID 1 i rebi correctament els senyals d'aturada. No la facis servir a la lleugera.
- Subshells i per què les variables no sobreviuen
Un subshell és un procés fill que també és un Bash. Se'n creen en més situacions de les que la gent sospita.
| Construcció | Crea subshell? | Exemple |
|---|---|---|
( ordres ) |
Sí | (cd /tmp; ls) |
| Canonada ` | ` | Sí, per a cada costat (a Bash per defecte) |
Substitució $( ) |
Sí | avui=$(date +%F) |
Segon pla & |
Sí | tasca & |
{ ordres; } |
No, agrupa al shell actual | { cd /tmp; ls; } |
source fitxer |
No | source ~/.bashrc |
./script.sh |
Sí | Executa un fitxer |
5.1 L'experiment clau
comptador=0
echo "Abans: $comptador"
( comptador=99; echo "Dins del subshell: $comptador" )
echo "Després: $comptador"Els parèntesis van crear un procés fill amb una còpia de les variables. Aquest fill va modificar la seva còpia i va morir. El pare no se'n va assabentar mai. No és cap fallada: és com funcionen els processos a Unix. La informació flueix del pare al fill, mai a l'inrevés.
Compara-ho amb les claus:
Les claus només agrupen; no hi ha cap procés nou. (Atenció a la sintaxi: les claus necessiten espais interiors i un ; abans de la de tancament.)
5.2 El cas que arruïna scripts de debò
Aquest és l'error clàssic, i el plantegem amb dades de Veloz Envíos. Volem comptar quantes línies d'app.log són errors:
errors=0
grep 'ERROR' /var/log/veloz/app.log | while read -r linia; do
errors=$((errors + 1))
done
echo "Errors trobats: $errors"Zero, tot i que sí que hi ha errors al fitxer. Per què? Perquè el costat dret de la canonada s'executa en un subshell. El bucle va incrementar correctament la seva pròpia còpia d'errors fins a, posem, 47; després el subshell va acabar i la còpia es va evaporar. L'echo final llegeix la variable del shell pare, que continua valent 0.
Hi ha tres solucions, i convé conèixer-les totes:
# Solució 1: redirecció d'entrada en lloc de canonada
errors=0
while read -r linia; do
errors=$((errors + 1))
done < <(grep 'ERROR' /var/log/veloz/app.log)
echo "Errors trobats: $errors"La construcció < <(...) s'anomena substitució de processos: el grep s'executa a part, però el bucle while corre al shell principal, de manera que la variable sobreviu. S'estudia a fons a 05-05.
# Solució 2: activar l'opció lastpipe (només Bash, requereix job control desactivat)
shopt -s lastpipe
errors=0
grep 'ERROR' /var/log/veloz/app.log | while read -r linia; do
errors=$((errors + 1))
done
echo "Errors trobats: $errors"Amb lastpipe, l'última ordre de la canonada s'executa al shell actual. Funciona en scripts, però no pas en sessions interactives normals.
# Solució 3: no fer servir el shell per comptar
errors=$(grep -c 'ERROR' /var/log/veloz/app.log)
echo "Errors trobats: $errors"Aquesta és la millor: grep -c compta pel seu compte i la substitució d'ordres en captura la sortida. Quan hi ha una eina que ja fa la feina, no la facis amb un bucle. És més ràpid, més curt i no té el problema del subshell.
5.3 El mateix principi explica source
Ara s'entén del tot allò que vam veure a 01-02:
./config.sh # executa en un subshell
echo "$CIUTAT" # buit
source config.sh # executa al shell actual
echo "$CIUTAT" # ValenciaI per això cd ha de ser un builtin: si fos un programa extern, s'executaria en un fill, canviaria el directori d'aquest fill i moriria. El teu directori no es mouria ni un mil·límetre. Aquella pregunta pendent de la lliçó anterior queda resolta.
- Entorn davant de variables de shell
Hi ha dues classes de variables, i la diferència és exactament la de la secció anterior: què s'hereta.
- Variable de shell: existeix només al shell actual. No es passa als fills.
- Variable d'entorn: es copia a l'entorn de cada procés fill.
var_local="només aquí"
export var_global="viatja als fills"
bash -c 'echo "var_local=[$var_local] var_global=[$var_global]"'El fill no va veure var_local perquè mai no es va exportar.
6.1 Eines per inspeccionar
| Ordre | Què mostra |
|---|---|
env |
Variables d'entorn (les exportades) |
printenv |
Igual que env; admet printenv NOM |
set |
Totes les variables i funcions del shell actual |
declare -p |
Variables amb el seu tipus i atributs |
export -p |
Només les exportades, amb la sintaxi declare -x |
/home/joan /home/joan/veloz-ops/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin joan
Variables d'entorn que et trobaràs sempre:
| Variable | Contingut |
|---|---|
HOME |
La teva carpeta personal |
USER |
El teu nom d'usuari |
PATH |
Directoris de cerca d'ordres |
PWD |
Directori actual |
OLDPWD |
Directori anterior (el que fa servir cd -) |
SHELL |
Shell d'inici de sessió configurat |
LANG |
Idioma i codificació |
TERM |
Tipus de terminal |
EDITOR |
Editor preferit |
6.2 Executar amb un entorn modificat
Pots definir una variable només per a una ordre, sense tocar el teu shell:
VELOZ_ENV=proves bash -c 'echo "Entorn: $VELOZ_ENV"'
echo "Al meu shell continua sent: [$VELOZ_ENV]"Aquesta sintaxi —assignació davant de l'ordre, sense ;— és netíssima i molt usada a la pràctica, per exemple per forçar un idioma a la sortida d'una ordre:
Amb LC_ALL=C la data surt en anglès. És un truc important en scripts: si el teu script analitza la sortida d'una ordre, força LC_ALL=C perquè no depengui de l'idioma del servidor. Un grep que cerca «ago» fallaria en una màquina configurada en anglès.
I per executar una cosa amb un entorn completament net:
env -i esborra tot l'entorn. És la millor manera de simular les condicions de cron, que executa amb un entorn mínim. Si el teu script funciona així, funcionarà a cron. Ho aplicarem a 07-01.
$?: el codi de sortida
$?: el codi de sortidaTota ordre retorna en acabar un número entre 0 i 255:
- 0 significa èxit.
- Qualsevol altre valor significa error.
És a l'inrevés del que suggereix la intuïció, però té sentit: hi ha una única manera d'encertar i moltes de fallar, així que cada valor diferent de zero pot identificar un tipus de fallada.
Codis amb significat convencional:
| Codi | Significat |
|---|---|
| 0 | Èxit |
| 1 | Error genèric |
| 2 | Ús incorrecte de l'ordre (arguments mal posats) |
| 126 | El fitxer existeix però no és executable |
| 127 | Ordre no trobada |
| 130 | Interromput amb Ctrl+C (128 + senyal 2) |
| 137 | Acabat amb kill -9 (128 + senyal 9) |
Comprovem-los:
Un detall que causa errors subtils: $? se sobreescriu amb cada ordre, inclòs l'echo que la mostra.
La segona lectura retorna 0 perquè és el resultat de l'echo anterior, que va funcionar bé. Si necessites el valor més d'un cop, desa'l immediatament:
Un cas especial que mereix atenció: en una canonada, $? és el codi de l'última ordre, no pas de la primera.
Encara que grep va fallar, wc -l va funcionar, així que la canonada declara èxit. És una font d'errors silenciosos en scripts. Es resol amb set -o pipefail o consultant l'array PIPESTATUS, temes de la lliçó 05-03.
Els codis de sortida són la base de &&, ||, if i del control d'errors en general. Aquí només era un primer contacte; l'ús complet arriba a 03-03 i 03-04.
- Posant-ho tot junt a Veloz Envíos
Un escenari realista que combina tot l'anterior. Un company et diu: «He escrit un script veloz-estat que compta els errors del registre, però retorna sempre 0 i, a més, quan el llanço des de cron diu que no troba l'ordre.»
Diagnostiquem-ho pas a pas.
Pas 1: què és realment veloz-estat?
Existeix i és al PATH. Però cron no llegeix el teu ~/.bashrc (ho vam veure a 01-02), així que el seu PATH no inclou ~/veloz-ops/bin. Simulem l'entorn de cron:
Reproduït. La solució: fer servir la ruta absoluta al crontab, o definir el PATH dins del mateix script.
Pas 2: per què compta 0?
L'script conté això:
total=0
grep 'ERROR' /var/log/veloz/app.log | while read -r linia; do
total=$((total + 1))
done
echo "Total d'errors: $total"Ja saps el diagnòstic: el while corre en un subshell i total no sobreviu. La correcció:
Pas 3: i si el fitxer no existeix?
Si el registre no existeix, grep retorna 2 i total queda buit. Desar el codi permet reaccionar-hi. A 03-04 aprendràs a convertir això en una condició, i a 05-03 a fer-ho de manera robusta.
Pas 4: verificació final de l'entorn
echo "PID del shell: $$"
echo "Ordre resolta: $(command -v veloz-estat)"
echo "Directori: $PWD"
VELOZ_ENV=produccio veloz-estat
echo "Sortida de l'script: $?"Aquest petit ritual —saber què s'executarà, amb quin entorn i què va retornar— és exactament el que distingeix algú que depura amb mètode d'algú que prova coses a l'atzar.
Errors Habituals i Consells
- Fer servir
whichper comprovar si una ordre existeix. No veu builtins ni funcions i el seu comportament varia entre sistemes. Fes servircommand -vals scripts itype -ade manera interactiva. - Esperar que una variable modificada dins d'una canonada sobrevisqui. El costat dret de
|és un subshell. Fes servir< <(...),lastpipeo, encara millor, una eina que ja faci el càlcul (grep -c,wc -l,awk). - Executar un fitxer de configuració amb
./config.shesperant que defineixi variables. Necessitessource config.sh. - Oblidar
exporti estranyar-se que l'script fill no vegi la variable. Senseexport, la variable no surt del shell actual. - Llegir
$?massa tard. Qualsevol ordre intermèdia la sobreescriu, inclòs unecho. Desa-la tan bon punt la necessitis. - Suposar que una canonada falla si falla la primera ordre.
$?reflecteix l'última. Fes servirset -o pipefail(05-03). - Modificar un script, moure'l i que Bash continuï executant la versió antiga. És la memòria cau de rutes:
hash -r. - Donar per fet que l'entorn de cron és el teu. No ho és. Prova els teus scripts amb
env -iabans de programar-los. - Consell: quan alguna cosa es comporti de manera inexplicable, fes-te aquestes tres preguntes per ordre: què és realment aquesta ordre (
type -a)?, s'està executant en un subshell?, quines variables hi ha a l'entorn (env)?. Cobreixen la majoria dels casos. - Consell:
bash -x script.shmostra cada línia després de les expansions, abans d'executar-la. És la manera més ràpida de veure el procés de la secció 1 en acció. Ho formalitzarem a 05-03.
Exercicis
Exercici 1: Classificar ordres
Sense executar res, prediu de quin tipus d'ordre es tracta en cada cas (àlies, keyword, funció, builtin o extern) i després verifica-ho amb type -a:
cdgrep[[exportforawk
Explica a més per què which cd no retorna res.
Exercici 2: El comptador que no compta
Aquest script pretén comptar quants enviaments amb estat incidencia hi ha a /srv/veloz/dades/enviaments.csv, però sempre imprimeix 0:
#!/usr/bin/env bash
incidencies=0
cat /srv/veloz/dades/enviaments.csv | while IFS=',' read -r id data ciutat repartidor estat import; do
if [ "$estat" = "incidencia" ]; then
incidencies=$((incidencies + 1))
fi
done
echo "Incidencies: $incidencies"- Explica exactament per què falla.
- Corregeix-lo de dues maneres diferents.
- Indica quina de les dues preferiries a
veloz-opsi per què.
Exercici 3: Entorn i codis de sortida
Respon executant el que calgui:
- Defineix una variable
CIUTAT="Bilbao"sense exportar i comprova si unbash -cfill la veu. Repeteix-ho exportant-la. - Executa una ordre que retorni el codi 127 i una altra que retorni el 2, i mostra tots dos codis.
- Executa
grep 'ERROR' /fitxer/que/no/existeix | wc -li explica per què$?val 0. - Comprova si
jqestà instal·lat al teu sistema fent servircommand -vdins d'unif.
Solucions
Solució a l'Exercici 1
cd és una ordre interna del shell grep és /usr/bin/grep [[ és una paraula clau del shell export és una ordre interna del shell for és una paraula clau del shell awk és /usr/bin/awk
| Ordre | Tipus | Raó |
|---|---|---|
cd |
Builtin | Ha de canviar el directori del shell actual |
grep |
Extern | Programa independent a /usr/bin |
[[ |
Keyword | Part de la sintaxi; Bash l'analitza de manera especial (per això no hi ha divisió en paraules a dins) |
export |
Builtin | Modifica l'entorn del shell actual |
for |
Keyword | Estructura de control del llenguatge |
awk |
Extern | És un llenguatge complet amb el seu propi intèrpret |
which cd no retorna res perquè which és un programa extern que només recorre els directoris del PATH cercant fitxers. cd no és cap fitxer: és codi dins del mateix Bash. Un procés extern no pot veure els builtins del seu pare.
Solució a l'Exercici 2
-
Per què falla: el bucle
whileés al costat dret d'una canonada, així que Bash l'executa en un subshell. Aquest subshell rep una còpia d'incidencies, la incrementa correctament i mor en acabar el bucle. El shell principal no veu mai el canvi, i l'echofinal llegeix la seva pròpia variable, que continua valent 0. És exactament el problema de la secció 5.2. -
Correcció A: substitució de processos (elimina la canonada, el bucle corre al shell principal):
#!/usr/bin/env bash
incidencies=0
while IFS=',' read -r id data ciutat repartidor estat import; do
if [ "$estat" = "incidencia" ]; then
incidencies=$((incidencies + 1))
fi
done < /srv/veloz/dades/enviaments.csv
echo "Incidencies: $incidencies"Fixa't que aquí ni tan sols cal < <(...): com que només volíem llegir un fitxer, n'hi ha prou amb una redirecció directa < fitxer. El cat original era innecessari (és l'anomenat ús inútil de cat).
Correcció B: delegar el recompte en una eina:
#!/usr/bin/env bash
incidencies=$(grep -c ',incidencia,' /srv/veloz/dades/enviaments.csv)
echo "Incidencies: $incidencies"O de manera més precisa, comprovant exactament el cinquè camp amb awk (Mòdul 6):
- Quina preferir: la B, amb
awk. Motius: és una sola línia, no té el problema del subshell i és molt més ràpida en fitxers grans. Un buclewhile readen Bash processa unes poques milers de línies per segon;awken processa centenars de milers. Amb unenviaments.csvd'11 MB la diferència és de segons davant de mil·lisegons. La regla general de Bash: el shell és per orquestrar, no per processar dades línia a línia. Ho veurem formalitzat a 08-02.
Solució a l'Exercici 3
# 1
CIUTAT="Bilbao"
bash -c 'echo "Sense export: [$CIUTAT]"'
export CIUTAT
bash -c 'echo "Amb export: [$CIUTAT]"'Sense export, CIUTAT és una variable de shell i no forma part de l'entorn que hereta el fill. Amb export, passa a formar part de l'entorn i viatja a la còpia.
127 és el codi convencional per a «ordre no trobada» i 2 per a «ús incorrecte».
$? val 0 perquè en una canonada reflecteix l'estat de l'última ordre. grep va fallar amb codi 2, però wc -l va rebre una entrada buida, va comptar zero línies i va acabar correctament. Per detectar la fallada real:
L'array PIPESTATUS desa el codi de cada element. L'alternativa habitual en scripts és set -o pipefail, que fa que la canonada retorni el primer codi diferent de zero (05-03).
# 4
if command -v jq > /dev/null 2>&1; then
echo "jq està instal·lat a $(command -v jq)"
else
echo "jq NO està instal·lat"
fiLa redirecció > /dev/null 2>&1 descarta tant la sortida normal com els errors: només ens interessa el codi de sortida de command -v, no pas el seu text. Les redireccions s'estudien a 02-04.
Conclusió
Ja tens el model mental que sosté tot el que ve. Saps que Bash trosseja la línia, l'expandeix, resol l'ordre en un ordre estricte (àlies → keyword → funció → builtin → PATH) i, si és externa, fa fork i exec per executar-la en un fill. Entens per què type -a és fiable i which no, com funciona la memòria cau del PATH i —el més rendible de tot— per què les variables no sobreviuen a un subshell, cosa que explica d'un cop el comportament de les canonades, els parèntesis, source i el mateix cd. I has tingut el teu primer contacte amb $?, la peça sobre la qual es construirà tot el control d'errors del curs.
Amb això tanques el coneixement fonamental del shell. Queda una habilitat transversal abans de passar a les ordres: saber resoldre dubtes pel teu compte. Cap professional no recorda totes les opcions de find ni el format exacte d'un crontab; el que sap és on mirar-ho en deu segons. A la lliçó següent, Trobar Ajuda: man, help i --help, aprendràs a llegir pàgines de manual, a distingir quan cal fer servir man i quan help, i a verificar ordres perilloses abans d'executar-les a srv-veloz-01.
Curs de Programació en Bash
Mòdul 1: Introducció a Bash
- Què és Bash?
- Configurar el teu Entorn
- Navegació Bàsica per la Línia d'Ordres
- Entendre el Shell
- Trobar Ajuda: man, help i --help
Mòdul 2: Ordres Bàsiques de Bash
- Operacions amb Fitxers i Directoris
- Ordres de Processament de Text
- Permisos i Propietat dels Fitxers
- Redirecció i Canonades
- Comodins i Expansió de Rutes
- Historial i Dreceres de Teclat
Mòdul 3: Fonaments de Scripting
- Crear i Executar un Script
- Variables i Constants
- Operadors Bàsics
- Sentències Condicionals
- Arguments i Entrada de l'Usuari
- Cometes, Expansió i Substitució
Mòdul 4: Scripting Intermedi
- Bucles en Bash
- Funcions en Bash
- Arrays i Arrays Associatius
- Manipulació de Cadenes
- La Sentència case i els Menús Interactius
- Aritmètica i Càlculs Numèrics
Mòdul 5: Tècniques Avançades de Scripting
- Operacions Avançades amb Fitxers
- Gestió de Processos
- Gestió d'Errors i Depuració
- Expressions Regulars
- Entrada/Sortida Avançada: Descriptors i Here-Documents
- Scripts Modulars i Llibreries Reutilitzables
Mòdul 6: Treballar amb Eines Externes
Mòdul 7: Automatització i Programació
- Tasques Cron
- Automatitzar Tasques
- Scripts de Còpia i Restauració
- Monitoratge i Registre
- Serveis i Temporitzadors amb systemd
- Automatització Remota amb SSH
Mòdul 8: Bones Pràctiques i Optimització
- Escriure Codi Llegible
- Optimitzar Scripts en Bash
- Consideracions de Seguretat
- Control de Versions amb Git
- Anàlisi Estàtica amb ShellCheck i shfmt
- Proves Automatitzades amb Bats
- Portabilitat: POSIX sh enfront de Bashismes
