A les tres lliçons anteriors has escrit if, case i [[ ]] guiant-te per la semblança amb altres llenguatges. Funcionava, però no sabies per què. Toca arreglar-ho començant per la idea que en Bash ho governa tot i que gairebé cap tutorial no enuncia amb claredat: aquí no es ramifica segons un valor booleà, es ramifica segons el codi de sortida d'una ordre. if no espera una expressió: espera una ordre, l'executa i mira si ha retornat 0; tota la resta —[ ], [[ ]], (( ))— són ordres que existeixen per produir aquell 0 o 1. Amb aquella peça al seu lloc, la resta encaixa sola. Al final, revisio_salut.sh sabrà dir OK, AVÍS o CRÍTIC, i naixerà purgar_releases.sh.
Contingut
- La condició és un codi de sortida
if,elif,else- Els tres tipus de test i quin fer servir
- Taula completa d'operadors
- Expressions regulars amb
=~iBASH_REMATCH casei els seus tres terminadors- Bucles:
for,while,until break,continueiselect- Curtcircuits
&&i||, i el seu parany - Bucles i rendiment: quan vols
awk - Aplicació: nivells d'alerta i
purgar_releases.sh
- La condició és un codi de sortida
operador@srv-tramontana:~$ if grep -q 'db_timeout' /var/log/tramontana/errors.log
> then echo "hi ha timeouts de base de dades"; fi
hi ha timeouts de base de dadesNo hi ha cap == true enlloc. if va executar grep -q, grep va retornar 0 perquè va trobar coincidències, i 0 significa èxit, així que es va entrar al then. Si no hagués trobat res hauria retornat 1 i s'hauria saltat el bloc.
Tres conseqüències que convé tenir presents des d'ara:
- Qualsevol ordre pot ser una condició:
if ping -c1 -W2 10.0.2.15,if [[ -f fitxer ]],if la_meva_funcio. Totes són el mateix. I el[clàssic és una ordre de debò, amb el seu executable a/usr/bin/[; per això[ $a = $b ]necessita espais: són arguments separats. - La lògica està invertida respecte als llenguatges amb booleans: 0 és cert. Si escrius una funció que comprova alguna cosa, ha de retornar 0 quan la resposta és «sí».
if, elif, else
if, elif, elseus_disc=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if (( us_disc >= 90 )); then
nivell="CRITIC"
elif (( us_disc >= 80 )); then
nivell="AVIS"
else
nivell="OK"
fiLa sintaxi exigeix then i fi; el ; abans de then permet posar-lo a la mateixa línia. Els elif s'avaluen en ordre i el primer que encerta guanya, així que les condicions van de la més restrictiva a la més laxa: si intercanviessis els dos primers blocs, un disc al 95 % diria AVÍS. Per negar, ! al davant: if ! systemctl is-active --quiet tramontana; then .... I compte amb if [ $? -eq 0 ], gairebé sempre redundant: posa l'ordre directament al if.
- Els tres tipus de test i quin fer servir
| Forma | Què és | Quan fer-la servir |
|---|---|---|
[ ... ] o test ... |
Ordre POSIX | Només si l'script ha de córrer en dash |
[[ ... ]] |
Paraula reservada de Bash | Sempre, per defecte |
(( ... )) |
Avaluació aritmètica | Comparacions i comptes numèrics |
La diferència entre [ ] i [[ ]] no és cosmètica. [[ ]] és sintaxi del mateix shell, no una ordre, així que Bash no divideix en paraules ni expandeix globs a dins:
operador@srv-tramontana:~$ f="informe agost.txt"
operador@srv-tramontana:~$ [ -f $f ] && echo existeix
-bash: [: informe: binary operator expected
operador@srv-tramontana:~$ [[ -f $f ]] && echo existeix || echo "no existeix, però sense error"
no existeix, però sense errorAmb [ ] la variable sense cometes es va partir en dos arguments i l'ordre ni tan sols va poder avaluar-se. Amb [[ ]] no cal entrecometar, i a més admet &&, ||, patrons i =~, que [ ] no té. Per a números, (( )) és molt més llegible: compara if (( errors > 20 && connexions >= 200 )) amb [ "$errors" -gt 20 -a "$connexions" -ge 200 ]. Recorda de 04-02 que dins de (( )) les variables van sense $.
- Taula completa d'operadors
| Grup | Operador | Cert si… |
|---|---|---|
| Fitxers | -e / -f / -d / -L |
Existeix / és fitxer regular / és directori / és enllaç |
| Fitxers | -s / -r / -w / -x |
No és buit / permís de lectura, escriptura, execució |
| Fitxers | a -nt b / a -ot b |
a és més nou / més vell que b |
| Cadenes | -z "$s" / -n "$s" |
És buida / no és buida |
| Cadenes | $s == patró |
Coincideix amb el patró de globbing (sense cometes a la dreta) |
| Cadenes | == / != / < / > |
Iguals / diferents / ordre lexicogràfic segons la locale |
| Cadenes | $s =~ regex |
Coincideix amb l'expressió regular (apartat 5) |
| Números | -eq -ne -lt -le -gt -ge |
Igual, diferent, menor, menor o igual, major, major o igual |
| Números | Dins de (( )) |
== != < <= > >=, i && || ! |
La confusió clàssica mereix un avís en negreta: -eq és per a números i == per a cadenes. [[ "01" -eq "1" ]] és cert (numèricament iguals) però [[ "01" == "1" ]] és fals (cadenes diferents). Fer servir l'operador equivocat dona resultats correctes durant mesos i després falla amb un zero a l'esquerra o amb una versió com ara 3.10.
Un altre detall que sorprèn: dins de [[ ]], el costat dret de == no s'entrecometa si vols que sigui un patró:
operador@srv-tramontana:~$ v="3.2.1"; [[ $v == 3.2.* ]] && echo "branca 3.2"
branca 3.2
operador@srv-tramontana:~$ [[ $v == "3.2.*" ]] && echo "branca 3.2" || echo "sense coincidència"
sense coincidènciaLes cometes converteixen el patró en text literal: és la font número u de «la meva comparació no funciona i no sé per què».
- Expressions regulars amb
=~ i BASH_REMATCH
=~ i BASH_REMATCH=~ compara amb una expressió regular estesa (l'ERE de 03-02) i, a més de dir sí o no, deixa les captures a la matriu BASH_REMATCH: la posició 0 és la coincidència completa i les següents, els grups entre parèntesis.
validar_data() {
local data="$1"
if [[ $data =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})$ ]]; then
printf 'vàlida: any %s, mes %s, dia %s\n' \
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
return 0
fi
printf 'format invàlid: %s\n' "$data" >&2
return 1
}
operador@srv-tramontana:~$ validar_data "2026-08-18"
vàlida: any 2026, mes 08, dia 18
operador@srv-tramontana:~$ validar_data "18/08/2026"; echo "codi: $?"
format invàlid: 18/08/2026
codi: 1Amb això valides la columna data de reserves.csv abans de fiar-te'n. Dues regles d'or: la regex no va entre cometes —si l'entrecometes es torna text literal, igual que amb ==— i si la guardes en una variable, fes-la servir també sense cometes: [[ $f =~ $PATRO ]].
case i els seus tres terminadors
case i els seus tres terminadorscase compara una cadena contra patrons de globbing —no expressions regulars— i executa la primera branca que hi encaixi.
classificar_codi() {
case "$1" in
2*) echo "èxit" ;; 3*) echo "redirecció" ;;
404) echo "no trobat" ;; 4*) echo "error del client" ;;
500|503) echo "error del servidor" ;; *) echo "desconegut" ;;
esac
}
operador@srv-tramontana:~$ for c in 200 404 418 503 999; do
> printf '%s->%s ' "$c" "$(classificar_codi "$c")"; done; echo
200->èxit 404->no trobat 418->error del client 503->error del servidor
999->desconegutL'ordre importa: 404 va abans que 4* perquè guanya el primer que hi encaixa. | separa alternatives i *) és el cas per defecte, que va l'últim i convé posar sempre. Els tres terminadors:
| Terminador | Efecte |
|---|---|
;; |
Acaba el case. El que faràs servir el 99 % de les vegades |
;& |
Cau a la branca següent sense comprovar-ne el patró (el fallthrough de C) |
;;& |
Continua comprovant els patrons restants i executa els que hi encaixin |
;;& és rar però útil per a etiquetes acumulatives; ;& és perillós perquè un lector distret no el veu. Fes-los servir només amb un comentari que ho justifiqui.
- Bucles:
for, while, until
for, while, untilfor sobre llistes i globs
for release in 3.1.0 3.2.0 3.2.1; do printf 'release %s\n' "$release"; done
for fitxer in /var/log/tramontana/*.log; do
[[ -e $fitxer ]] || continue # protegeix del glob sense coincidències
printf '%-40s %s línies\n' "$fitxer" "$(wc -l < "$fitxer")"
doneLa segona forma —iterar sobre un glob— és la correcta per recórrer fitxers, i porta dos avisos. El primer: si el glob no troba res, Bash deixa el patró literal i el bucle s'executa una vegada amb /var/log/tramontana/*.log com a valor; d'aquí el [[ -e ... ]] || continue, o bé shopt -s nullglob de 03-02. El segon, més important:
No escriguis mai
for f in $(ls).
Ho veuràs a internet constantment i està malament per tres motius acumulatius: ls retorna text que el shell parteix per espais, així que «informe agost.txt» es converteix en dos elements; els caràcters especials del nom s'expandeixen com a globs; i ls pot acolorir o alinear segons opcions i àlies. El glob no té cap d'aquells problemes perquè Bash treballa amb noms de fitxer reals, no amb text. I existeix la forma aritmètica, for (( i = 0; i < ${#versions[@]}; i++ )), per a quan l'índex importa.
while i until
while repeteix mentre la condició retorni 0; until repeteix fins que retorni 0. Són la mateixa estructura amb la condició negada, i until guanya quan la frase natural és «espera fins que»:
intents=0
until curl -sf -o /dev/null --max-time 3 "http://10.0.2.15:8080/salut"; do
(( ++intents ))
(( intents >= 10 )) && { echo "l'aplicació no arrenca" >&2; exit 69; }
sleep 2
done
printf 'ha respost després de %d intents\n' "$intents"Aquell bucle és el que necessita un desplegament per esperar que el servei estigui a punt, i el reutilitzarem a 04-07. Ja coneixes a més el while read de 04-03, que és el tercer ús habitual.
break, continue i select
break, continue i selectbreak surt del bucle i continue salta a la iteració següent. Tots dos accepten un número que indica quants nivells afecten: break 2 surt de dos bucles imbricats de cop.
for dir in /opt/tramontana/releases/*/; do
for fitxer in "$dir"*.conf; do
[[ -e $fitxer ]] || continue
grep -q 'db_password' "$fitxer" || continue
printf 'credencial a %s\n' "$fitxer" >&2
break 2 # deixa de cercar a tots els releases
done
done
PS3="Tria un release per desplegar: " # select: menú numerat de franc
select v in 3.2.1 3.3.0 cancella; do
[[ -n $v ]] || { echo "opció no vàlida" >&2; continue; }
[[ $v == cancella ]] && break
printf 'desplegant %s\n' "$v"; break
doneSi et trobes amb tres nivells d'imbricació, això no és un problema de bucles: és una funció que encara no has escrit, i ho veurem a 04-05. Quant a select, és un bucle i sense break no acaba mai; PS3 és el text de l'avís i $REPLY guarda el que va teclejar l'usuari.
- Curtcircuits
&& i ||, i el seu parany
&& i ||, i el seu paranyJa els fas servir des de 02-01: A && B executa B només si A ha tingut èxit; A || B executa B només si A ha fallat. Com a condicional d'una línia són insuperables: [[ -d $DIR ]] || mkdir -p "$DIR", o command -v shellcheck >/dev/null || { echo "falta shellcheck" >&2; exit 69; }. Però molta gent escriu condicio && accio || altra_cosa creient que és un if/else, i no ho és:
La clau: || no mira la condició, mira el resultat de l'acció anterior. Si l'acció falla per qualsevol motiu —un mkdir sense permís, un grep sense coincidències— la branca del || s'executa a més de la del &&. La regla: encadena && i || només quan l'acció no pugui fallar; tan bon punt hi hagi un else de debò, fes servir if.
- Bucles i rendiment: quan vols
awk
awkAquest apartat t'estalviarà hores d'espera. Comptem les 14 respostes 500 d'acces.log de dues maneres:
operador@srv-tramontana:~$ time (n=0; while read -r l; do
> grep -q ' 500 ' <<<"$l" && (( ++n )); done < /var/log/tramontana/acces.log
> echo "$n")
14
real 0m1,873s
operador@srv-tramontana:~$ time awk '$5 == 500 { n++ } END { print n+0 }' \
> /var/log/tramontana/acces.log
14
real 0m0,006sTres-centes vegades més ràpid, i només amb 412 línies. La causa no és que Bash interpreti a poc a poc: és que el bucle llança un procés grep per cada línia, i crear un procés costa uns mil·lisegons que es multipliquen. Amb les 10.000 línies que tindrà acces.log en un mes, el bucle trigaria gairebé un minut i awk continuaria en centèsimes. La regla:
- Si el bucle processa text línia a línia, probablement vols
awk(ogrep,sed,sort), que fan una sola passada dins d'un únic procés. - Si el bucle ha de cridar una ordre externa a cada volta —
curl,systemctl,tar— aleshores sí que és feina de Bash, perquè no hi ha alternativa. I si necessites Bash sobre text, almenys no llancis processos a dins:readamb diversos noms,casei expansió de paràmetres fan molt sense sortir del shell.
- Aplicació: nivells d'alerta i
purgar_releases.sh
purgar_releases.shrevisio_salut.sh per fi decideix. Substituïm el bloc d'impressió per comprovacions amb nivell:
# --- Avaluació -----------------------------------------------------------
nivell_global="OK"
pitjor() { # eleva el nivell global si el nou és més greu
case "$1" in
CRITIC) nivell_global="CRITIC" ;;
AVIS) [[ $nivell_global == OK ]] && nivell_global="AVIS" ;;
esac
}
case "$codi_http" in
200) estat_http="OK" ;;
000) estat_http="CRITIC" ;; # curl no va arribar ni a connectar
*) estat_http="AVIS" ;;
esac
if (( us_disc >= 90 )); then estat_disc="CRITIC"
elif (( us_disc >= LLINDAR_DISC )); then estat_disc="AVIS"
else estat_disc="OK"; fi
if (( errors_avui >= 50 )); then estat_err="CRITIC"
elif (( errors_avui >= 10 )); then estat_err="AVIS"
else estat_err="OK"; fi
pitjor "$estat_http"; pitjor "$estat_disc"; pitjor "$estat_err"
LC_ALL=C printf '%-10s %-8s %s\n' \
"HTTP" "$estat_http" "codi $codi_http" \
"Disc" "$estat_disc" "${us_disc}% usat (llindar ${LLINDAR_DISC}%)" \
"Errors" "$estat_err" "$errors_avui avui"
printf '\nEstat global: %s\n' "$nivell_global"
case "$nivell_global" in OK) exit 0 ;; AVIS) exit 1 ;; CRITIC) exit 2 ;; esac
operador@srv-tramontana:~$ ~/scripts/revisio_salut.sh; echo "codi: $?"
HTTP OK codi 200
Disc OK 30% usat (llindar 80%)
Errors OK 6 avui
Estat global: OK
codi: 0
operador@srv-tramontana:~$ ~/scripts/revisio_salut.sh -u 25 | tail -1
Estat global: AVISAmb aquells codis —0 correcte, 1 avís, 2 crític— l'script ja serveix per a monitoració: Nagios, Zabbix i companyia fan servir exactament aquella convenció. I l'script nou:
#!/usr/bin/env bash
#
# purgar_releases.sh - Elimina releases antics conservant l'actiu,
# l'anterior i els més nous.
# Autor : Operador de sistemes <operador@srv-tramontana> Data: 2026-08-18
# Ús : purgar_releases.sh [-n] [-v] Sortida: 0 ok | 2 ús | 66 sense releases
set -euo pipefail
readonly BASE="/opt/tramontana"
readonly DIR_RELEASES="$BASE/releases"
DRY_RUN=0; VERBOSE=0
# log(), error() i executar() són les de 04-03; getopts amb ":hnv" fixa -n i -v.
actiu=$(basename "$(readlink -f "$BASE/app")") # cap on apunta l'enllaç
log "release actiu: $actiu"
# Iterem sobre el glob (mai sobre ls) i ordenem amb sort -V, que sap que
# 3.10.0 va després de 3.9.0, cosa que sort a seques ignora. mapfile aboca
# cada línia en un element de la matriu, sense partir per espais.
llista=()
for d in "$DIR_RELEASES"/*/; do [[ -d $d ]] && llista+=("$(basename "$d")"); done
(( ${#llista[@]} )) || { error "no hi ha releases a $DIR_RELEASES"; exit 66; }
mapfile -t versions < <(printf '%s\n' "${llista[@]}" | sort -V)
index_actiu=-1 # posició de l'actiu, per saber quin és "l'anterior"
for (( i = 0; i < ${#versions[@]}; i++ )); do
[[ ${versions[i]} == "$actiu" ]] && index_actiu=$i
done
(( index_actiu >= 0 )) || { error "l'actiu $actiu no és a la llista"; exit 66; }
for (( i = 0; i < ${#versions[@]}; i++ )); do
v="${versions[i]}"
(( i >= index_actiu - 1 )) && { log "conservo $v"; continue; }
printf 'purgant %s (%s)\n' "$v" "$(du -sh "$DIR_RELEASES/$v" | cut -f1)"
executar rm -rf -- "${DIR_RELEASES:?}/$v"
done
exit 0operador@srv-tramontana:~$ ~/scripts/purgar_releases.sh -n -v
[2026-08-18 11:04:12] release actiu: 3.2.1
purgant 3.1.0 (97M)
[simulat] rm -rf -- /opt/tramontana/releases/3.1.0
[2026-08-18 11:04:12] conservo 3.2.0
[2026-08-18 11:04:12] conservo 3.2.1
[2026-08-18 11:04:12] conservo 3.3.0Just el que volíem: només 3.1.0 és purgable. Fixa't en dos detalls defensius: rm -rf -- "${DIR_RELEASES:?}/$v" fa servir ${VAR:?} de 04-02 perquè, si la variable quedés buida per un error, l'ordre avorti en comptes de convertir-se en rm -rf /; i -- evita que un nom de directori amb guionet inicial es llegeixi com a opció.
Errors Comuns i Consells
- Oblidar els espais dins de
[ ]o[[ ]].[[$a == $b]]donacommand not found: els claudàtors són ordres i paraules reservades, no sintaxi enganxada. - Barrejar
-eqamb cadenes o==amb números.[[ "3.10" > "3.9" ]]és fals lexicogràficament: per a versions,sort -V; per a números,(( )). I no entrecometis el costat dret de==o=~, o el patró es torna text literal. for f in $(ls). Es trenca amb espais i amb caràcters especials; fes servir sempre el glob. I protegeix el glob sense coincidències amb[[ -e $f ]] || continueoshopt -s nullglob, o el bucle correrà una vegada amb el patró literal.- Confiar en
cond && accio || altracom aif/else. Siacciofalla, s'executen les dues branques. I imbricar tres bucles és el senyal inequívoc que falta una funció. - Consell: compara números sempre amb
(( )): és més llegible i no confondràs l'operador. Posa sempre la branca*)en uncase, encara que només sigui per registrar «valor inesperat». I abans d'escriure un bucle que processi text, pregunta't siawkho faria d'una passada: gairebé sempre la resposta és sí.
Exercicis
Exercici 1. Escriu es_versio_valida(), que retorni 0 si el seu argument té el format N.N.N (un o més dígits per tram) i 1 si no, fent servir =~, i que imprimeixi els tres números per separat. Prova-la amb 3.2.1, 3.10.0, 3.2 i v3.2.1.
Exercici 2. Escriu ~/scripts/resum_franges.sh, que sobre acces.log classifiqui cada petició en matinada (00-05), matí (06-13), tarda (14-21) o nit (22-23) amb case, i mostri peticions i errors per franja. Fes-ho sense llançar processos dins del bucle i compara el temps amb awk.
Exercici 3. En Luis ha escrit aquest fragment per decidir si cal purgar. Troba-hi els quatre defectes i reescriu-lo.
lliure=`df / | tail -1 | awk '{print $5}'`
if [ $lliure > 80 ]; then
echo "purgant" && rm -rf /opt/tramontana/releases/* || echo "error"
fiSolucions
Solució 1.
es_versio_valida() {
local v="$1"
# ^ i $ ancoren la coincidència a TOTA la cadena: sense ells, "v3.2.1x"
# també hi encaixaria. Els parèntesis creen els grups de BASH_REMATCH.
[[ $v =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]] || return 1
printf 'major=%s menor=%s pedaç=%s\n' \
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
}
for v in 3.2.1 3.10.0 3.2 v3.2.1; do
if es_versio_valida "$v"; then printf ' %-8s vàlida\n' "$v"
else printf ' %-8s NO vàlida\n' "$v"; fi
done
operador@srv-tramontana:~$ bash /tmp/versions.sh
major=3 menor=2 pedaç=1
3.2.1 vàlida
major=3 menor=10 pedaç=0
3.10.0 vàlida
3.2 NO vàlida # falta un tram
v3.2.1 NO vàlida # l'àncora ^ rebutja la 'v'Els punts van escapats (\.) perquè en una regex . significa «qualsevol caràcter»; sense escapar, 3x2y1 també seria vàlida. I fixa't en com la funció es fa servir directament al if: és l'apartat 1 en acció. (Sense return 0 explícit, la funció retorna el codi del printf, que és 0.)
Solució 2.
#!/usr/bin/env bash
#
# resum_franges.sh - Peticions i errors per franja horària a acces.log.
# Autor : Operador de sistemes <operador@srv-tramontana> Data: 2026-08-18
set -euo pipefail
readonly LOG="${1:-/var/log/tramontana/acces.log}"
declare -A peticions errors
# read trosseja la línia sense llançar processos: l'hora és el segon camp i
# el codi el cinquè. ${hora%%:*} deixa només HH, i el 10# força base
# decimal perquè "08" i "09" serien octal invàlid (04-02).
while read -r _data hora _metode _ruta codi _resta; do
case "$(( 10#${hora%%:*} ))" in
[0-5]) franja="matinada" ;;
[6-9]|1[0-3]) franja="matí" ;;
1[4-9]|2[01]) franja="tarda" ;;
*) franja="nit" ;;
esac
peticions["$franja"]=$(( ${peticions["$franja"]:-0} + 1 ))
[[ $codi == [45]* ]] && errors["$franja"]=$(( ${errors["$franja"]:-0} + 1 ))
done < "$LOG"
LC_ALL=C printf '%-12s %12s %8s\n' FRANJA PETICIONS ERRORS
for f in matinada matí tarda nit; do
LC_ALL=C printf '%-12s %12d %8d\n' "$f" "${peticions[$f]:-0}" "${errors[$f]:-0}"
done
operador@srv-tramontana:~$ time ~/scripts/resum_franges.sh
FRANJA PETICIONS ERRORS
matinada 88 15
matí 147 3
tarda 141 4
nit 36 1
real 0m0,048sEls 15 errors de matinada són els de la franja de les 03:00 que ja coneixes, i els 23 totals quadren. La versió en awk —una línia amb substr($2,1,2)+0 i el ternari per a la franja— triga 0,007 s: set vegades menys tot i no haver-hi processos dins del bucle, només pel cost de l'intèrpret. Però fixa't en l'escala: centèsimes contra centèsimes. La diferència catastròfica apareix quan hi ha $(...) dins del bucle, no pel bucle en si. I nota el patró [45]* del case: globbing, no regex.
Solució 3. Els quatre defectes:
` `en comptes de$( ). Les cometes invertides no s'imbriquen, tenen regles d'escapament diferents i són il·legibles. A més$5dedfinclou el%, així que la comparació numèrica rep30%.[ $lliure > 80 ]. Dins de[ ],>no compara: és una redirecció, així que aquella ordre crea un fitxer anomenat80al directori actual i el test sempre és cert. Cal fer servir-gto, millor,(( )).rm -rf /opt/tramontana/releases/*esborra tot, inclòs el release actiu al qual apunta l'enllaçapp: l'aplicació cau a l'acte.echo ... && rm ... || echo "error". Si elrmfalla parcialment s'imprimeixen tots dos missatges i l'script no retorna cap codi útil. A més no hi ha--dry-runni registre de què s'ha esborrat.
us_disc=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if (( us_disc > 80 )); then
printf 'disc al %d%%, purgant releases antics\n' "$us_disc" >&2
if ~/scripts/purgar_releases.sh -v; then printf 'purga completada\n' >&2
else printf 'la purga ha fallat (codi %d)\n' "$?" >&2; exit 1; fi
fiLa lògica destructiva es delega a purgar_releases.sh, que ja sap què conservar, té --dry-run i registra el que fa. Reutilitzar en comptes de reescriure també és una estructura de control.
Conclusió
Ja no escrius condicionals per imitació: saps què passa per sota.
- Tens clar que la condició és un codi de sortida, que 0 és èxit, i que qualsevol ordre —inclosa una funció teva— pot anar en un
if. - Fas servir
if/elif/elseordenant de la condició més restrictiva a la més laxa, i tries entre els tres tipus de test:[[ ]]per defecte perquè no divideix paraules i admet patrons,(( ))per al que és numèric, i[ ]només si necessites POSIX. - Maneges la taula completa d'operadors de fitxer, cadena i número; no confons
-eqamb==ni entrecometes el costat dret d'un patró; valides amb=~iBASH_REMATCH, amb la regex sense cometes i ancorada amb^i$; i escriuscaseamb patrons de globbing, el*)obligatori i els terminadors;;,;&i;;&. - Recorres amb
forsobre globs, mai sobre$(ls), amb la forma aritmètica quan l'índex importa, i ambwhile/untilper a esperes i lectures; controles el flux ambbreak N,continue Niselect. Coneixes el parany decond && accio || altrai la regla de rendiment: si el bucle processa text, probablement volsawk, mesurat en un cas real. - I els teus scripts decideixen:
revisio_salut.shretorna OK, AVÍS o CRÍTIC amb codis aptes per a monitoració, ipurgar_releases.shrecorre els releases conservant l'actiu, l'anterior i els més nous.
Mira ara els scripts junts: log() està copiada tres vegades, error() també, l'embolcall executar() va per la seva segona còpia i el bloc de getopts és gairebé idèntic a tots. Cada vegada que en milloris un t'hauràs de recordar de millorar els altres, i algun dia no te'n recordaràs. A la lliçó següent, Funcions i Biblioteques, resolem això: quan extreure una funció i per què el nom és documentació, l'àmbit dinàmic de Bash i per què tota variable ha de ser local, les tres formes reals de retornar dades quan return només admet un número de 0 a 255, i com muntar lib/comuns.sh amb càrrega robusta, guardes d'inclusió i fins i tot un petit test que comprovi que la biblioteca funciona. En acabar-la, aquelles funcions duplicades viuran en un sol lloc.
Curs de Linux: De Principiant a Administrador de Sistemes
Mòdul 1: Introducció a Linux
- Què és Linux?
- Història de Linux
- Distribucions de Linux
- Instal·lant Linux
- Primer Contacte amb el Sistema
- Estructura del Sistema de Fitxers de Linux
Mòdul 2: Comandes Bàsiques de Linux
- Introducció a la Línia de Comandes
- Obtenir Ajuda i Documentació del Sistema
- Navegant pel Sistema de Fitxers
- Operacions amb Fitxers i Directoris
- Visualització i Edició de Fitxers
- Enllaços Durs i Simbòlics
- Permisos i Propietat dels Fitxers
Mòdul 3: Habilitats Avançades en la Línia de Comandes
- L'Entorn del Shell: Variables, Àlies i Historial
- Ús de Comodins i Expressions Regulars
- Cerca de Fitxers i Contingut: find, locate i grep
- Canonades i Redirecció
- Processament de Text: cut, sort, uniq, sed i awk
- Gestió de Processos
- Programació de Tasques amb Cron
- Comandes de Xarxa
Mòdul 4: Scripting en Shell
- Introducció al Scripting en Shell
- Variables i Tipus de Dades
- Entrada, Sortida i Arguments d'un Script
- Estructures de Control
- Funcions i Biblioteques
- Depuració i Gestió d'Errors
- Scripts de Producció: Bones Pràctiques
Mòdul 5: Administració del Sistema
- Gestió d'Usuaris i Grups
- sudo i Permisos Especials
- Gestió de Paquets
- Gestió de Discs
- systemd i la Gestió de Serveis
- Registres del Sistema: journald i syslog
- Monitoratge del Sistema i Optimització del Rendiment
- Còpies de Seguretat i Restauració
Mòdul 6: Xarxes i Seguretat
- Configuració de Xarxes
- SSH i Accés Remot
- Tallafocs i Seguretat Perimetral
- Sistemes de Detecció d'Intrusions
- Gestió de Secrets i Certificats TLS
- Assegurant Sistemes Linux
Mòdul 7: Temes Avançats
- El Procés d'Arrencada i la Recuperació del Sistema
- Diagnòstic Avançat: strace, perf i eBPF
- Optimització del Nucli de Linux
- Virtualització amb Linux
- Contenidors de Linux i Docker
- Automatització amb Ansible
- Alta Disponibilitat i Balanceig de Càrrega
