Al final de 08-01 vam substituir un bucle while read per un únic awk afirmant que era «més ràpid». És una afirmació còmoda i molt repetida, però mentre no hi hagi un número al costat no és enginyeria, és folklore. Aquesta lliçó va de números: com es mesuren de debò els temps d'un script, què és el que de veritat costa car en Bash —i no és el que la majoria es pensa—, quines optimitzacions tenen un efecte mesurable i quines són superstició, i sobretot quan s'ha de deixar d'optimitzar. Al final perfilarem informe-diari.sh sobre un CSV de 500.000 enviaments i reduirem el seu temps d'execució de minuts a segons, mesurant abans i després.

Contingut

  1. Primer mesurar, després optimitzar
  2. Les tres maneres de mesurar: time, SECONDS i date +%s%N
  3. El cost dominant en Bash: crear processos
  4. Builtins davant d'ordres externes
  5. Canonades inútils i llegir el fitxer una sola vegada
  6. El cost ocult de les subshells
  7. Acumular la sortida i escriure un sol cop
  8. while read, mapfile o awk: quan cadascun
  9. Paral·lelitzar de debò, i quan no serveix
  10. LC_ALL=C, memòria i el criteri final
  11. Aplicació: perfilar informe-diari.sh

  1. Primer mesurar, després optimitzar

Hi ha tres raons per no optimitzar sense mesurar. La primera és que la intuïció falla: gairebé tothom es pensa que el bucle és lent per ser un bucle, quan en realitat ho és pels dos cut que hi ha a dins. La segona és que optimitzar costa llegibilitat, i acabes de dedicar una lliçó sencera a guanyar-la; pagar-la a canvi d'un 2 % de millora és un mal negoci. La tercera és que el 90 % del temps sol estar en el 10 % del codi: si no saps quin és aquest 10 %, optimitzaràs la resta.

El mètode és sempre el mateix, quatre passos:

  1. Estableix la línia base. Mesura l'script tal com està, sobre dades representatives.
  2. Localitza el punt calent. Mesura per parts fins a saber quin bloc s'emporta el temps.
  3. Canvia una sola cosa i torna a mesurar en les mateixes condicions.
  4. Atura't quan ja n'hi hagi prou. Si l'informe diari ja triga 4 segons i s'executa un cop al dia, baixar-lo a 2 no li serveix a ningú.

I una advertència sobre les «dades representatives»: mesurar amb el CSV de proves de 50 línies no diu absolutament res. Moltes optimitzacions només es noten a partir d'una certa mida, i algunes —carregar el fitxer en memòria— són bones amb 1.000 línies i catastròfiques amb deu milions.

  1. Les tres maneres de mesurar: time, SECONDS i date +%s%N

$ time ./informe-diari.sh > /dev/null

real    0m47,312s
user    0m21,088s
sys     0m24,905s

Les tres xifres expliquen històries diferents i saber-les llegir és mitja lliçó:

Xifra Què mesura Què significa si és alta
real Temps de rellotge transcorregut El que pateix l'usuari; inclou esperes de disc i de xarxa
user CPU gastada en el teu codi i el de les ordres Càlcul pur: bucles, awk, sort
sys CPU gastada dins del nucli Moltes crides al sistema: crear processos, obrir fitxers

La combinació diagnòstica és aquesta: si user + sys és molt inferior a real, l'script està esperant (disc, xarxa, un sleep) i optimitzar el codi no servirà de res. I si sys és alt i comparable a user, com a l'exemple, el culpable gairebé sempre és la creació de processos: 24 segons dins del nucli no els gasta un càlcul, els gasta un fork per línia.

Compte amb un detall: time és una paraula clau del shell i una ordre externa (/usr/bin/time), amb sortides diferents. La del shell és la que has vist; l'externa, amb -v, hi afegeix la memòria màxima utilitzada, que és molt útil. Per mesurar parts d'un script, dues eines ja conegudes:

SECONDS=0                                      # variable magica de Bash (04-06)
processa_enviaments "$fitxer"
veloz_log_info "processat en ${SECONDS}s"      # resolucio d'1 segon

inici=$(date +%s%N)                            # nanosegons
veloz_percentatge 412 1284 >/dev/null
printf 'ha trigat %d ms\n' $(( ($(date +%s%N) - inici) / 1000000 ))

SECONDS és gratis (és intern) però només dóna segons sencers: serveix per a blocs llargs. date +%s%N dóna nanosegons i serveix per a blocs curts, tot i que crea un procés per mesura, així que no el posis dins del bucle que estàs mesurant. I una regla de laboratori: repeteix la mesura tres cops i queda't amb la mediana. La primera execució omple la memòria cau de disc i sempre és la més lenta; comparar una primera execució amb una tercera és la manera més habitual de «demostrar» una millora inexistent.

  1. El cost dominant en Bash: crear processos

Aquesta és la idea de la lliçó. En Bash, executar una ordre externa implica que el nucli dupliqui el procés (fork), carregui el binari (exec), resolgui les seves biblioteques i el destrueixi en acabar. Això costa de l'ordre d'1 a 3 mil·lisegons. Sembla res, fins que ho multipliques pel nombre de línies d'un fitxer.

Comparem tres maneres d'extreure la ciutat (camp 3) de cada línia d'un CSV de 500.000 enviaments:

# A) Un proces extern per linia: l'anti-patro
while IFS= read -r linia; do
    ciutat=$(echo "$linia" | cut -d, -f3)
    (( comptador[$ciutat]++ ))
done < enviaments.csv

# B) Sense processos: separacio per IFS, tot dins del shell (04-01)
while IFS=, read -r id data ciutat resta; do
    (( comptador[$ciutat]++ ))
done < enviaments.csv

# C) Un unic proces per a tot el fitxer (06-01)
awk -F, 'NR > 1 { c[$3]++ } END { for (x in c) print x, c[x] }' enviaments.csv

Els temps mesurats sobre el mateix fitxer i la mateixa màquina:

Versió real Processos creats Factor
A) echo | cut per línia 21 min 40 s 1.000.000 ×1
B) read amb IFS=, 12,4 s 0 ×105
C) awk d'una passada 0,9 s 1 ×1.400

Un milió de processos per llegir mig milió de línies. La versió A no és «una mica més lenta»: és inviable. I fixa't que la diferència entre A i B no és el bucle, que és el mateix, sinó haver tret els dos processos de dins. Aquesta és la primera regla operativa: mai executis una ordre externa dins d'un bucle que recorre línies. Si la necessites, probablement el bucle sencer hagi de ser un awk.

  1. Builtins davant d'ordres externes

Corol·lari de l'anterior: quan el shell pot fer alguna cosa per si mateix, fes-la amb ell. type -a (01-04) et diu quin és quin.

En comptes de (extern) Fes servir (intern) Nota
test, [ ] [[ ]] En Bash [ també és builtin, però [[ ]] no expandeix
expr $a + $b $(( a + b )) expr és un fòssil; $(( )) és intern (04-06)
echo amb opcions printf printf és builtin i, a més, portable (03-06)
wc -c <<< "$s" ${#s} Longitud sense procés ni here-string
basename "$f" ${f##*/} Expansió de paràmetre (04-04)
dirname "$f" ${f%/*} Ídem
echo "$s" | tr a-z A-Z ${s^^} Bash 4+ (04-04)
echo "$s" | sed 's/a/b/' ${s/a/b} Per a casos simples
cat fitxer (un cop) $(< fitxer) Bash llegeix el fitxer sense llançar cat
seq 1 100 {1..100} Expansió de claus

Aquí convé una dosi d'honestedat: fora d'un bucle, això és irrellevant. Canviar un basename solt per ${f##*/} estalvia dos mil·lisegons en tot l'script i pot empitjorar la llegibilitat per a qui no domini les expansions. La taula importa dins dels bucles, on l'estalvi es multiplica pel nombre de voltes. Optimitza l'interior dels bucles i deixa l'exterior llegible.

  1. Canonades inútils i llegir el fitxer una sola vegada

Cada | d'una canonada és un procés més. La majoria de les canonades llargues que es veuen en producció tenen etapes que sobren:

cat app.log | grep ERROR | wc -l              # 3 processos
grep -c ERROR app.log                         # 1 proces, mateix resultat

cat enviaments.csv | awk -F, '{print $3}'     # 2 processos
awk -F, '{print $3}' enviaments.csv           # 1 proces, awk obre fitxers

grep ERROR app.log | awk '{print $4}'         # 2 processos i dues passades
awk '/ERROR/ {print $4}' app.log              # 1 proces, awk ja filtra

sort f | uniq | sort -rn                      # ordena dos cops
sort -u f | sort -rn                          # una menys

El cas del cat inútil fins i tot té nom (useless use of cat). Però l'error més car no és comptar processos: és llegir el mateix fitxer diverses vegades. Aquest patró apareix a tots els scripts d'informes:

# MALAMENT: quatre passades completes sobre 500.000 linies
total=$(wc -l < enviaments.csv)
lliurats=$(grep -c ',lliurat,' enviaments.csv)
incidencies=$(grep -c ',incidencia,' enviaments.csv)
valencia=$(awk -F, '$3 == "Valencia"' enviaments.csv | wc -l)

# CORRECTE: una sola passada, quatre resultats (06-01)
read -r total lliurats incidencies valencia < <(awk -F, '
    NR > 1 { total++
             if ($5 == "lliurat")    lliurats++
             if ($5 == "incidencia") incidencies++
             if ($3 == "Valencia")   valencia++ }
    END { print total+0, lliurats+0, incidencies+0, valencia+0 }' enviaments.csv)

La primera versió llegeix 500.000 línies quatre vegades; la segona, una. Al toolkit real això va baixar el bloc d'estadístiques de 3,2 s a 0,8 s. I la versió d'una passada és, a més, més consistent: si el fitxer canvia mentre s'executa, les quatre xifres de dalt poden no quadrar entre si.

  1. El cost ocult de les subshells

$( ) crea una subshell, que és un procés fill amb tota la seva memòria copiada. Costa el mateix que llançar una ordre externa, encara que a dins no n'hi hagi cap:

for id in "${ids[@]}"; do
    data=$(date -d "@$marca" +%F)       # 2 processos per volta: subshell + date
done

printf -v data '%(%F)T' "$marca"        # 0 processos: format de data intern (Bash 4.2+)

El mateix passa amb les canonades: cmd | while read ... executa el while en una subshell, i per això les variables que modifiques a dins no existeixen en sortir —el problema clàssic que vam resoldre a 05-05 amb done < <(cmd)—. Allà la substitució de procés no és només qüestió de correcció: també estalvia un procés.

Un truc útil per a blocs que escriuen molt: en lloc d'agrupar amb ( ... ) > sortida.txt, que crea subshell, agrupa amb claus, que no la crea:

{ capcalera; cos; peu; } > informe.txt        # sense subshell

  1. Acumular la sortida i escriure un sol cop

Cada >> dins d'un bucle obre el fitxer, escriu i el tanca. Amb 500.000 voltes són 500.000 obertures:

# MALAMENT: una obertura per linia
for enviament in "${enviaments[@]}"; do
    printf '%s\n' "$enviament" >> informe.txt
done

# CORRECTE: acumula en un array i escriu un cop (04-03)
linies=()
for enviament in "${enviaments[@]}"; do
    linies+=( "$enviament" )
done
printf '%s\n' "${linies[@]}" > informe.txt

# ENCARA MILLOR: redirigeix el bucle sencer, una sola obertura i sense memoria extra
for enviament in "${enviaments[@]}"; do
    printf '%s\n' "$enviament"
done > informe.txt

Les tres produeixen el mateix fitxer; la tercera és la més ràpida, la més curta i la que menys memòria fa servir, perquè el descriptor s'obre un cop i el bucle escriu al flux (05-05). Mesurat amb 200.000 línies: 4,1 s la primera, 0,9 s la segona, 0,7 s la tercera.

  1. while read, mapfile o awk: quan cadascun

Tècnica Memòria Velocitat Quan utilitzar-la
while IFS= read -r l Constant Mitjana Fitxers grans, processament línia a línia, lògica que necessita el shell
mapfile -t arr < f Tot el fitxer a la RAM Alta Fitxers petits (< 50.000 línies) que has de recórrer diverses vegades o indexar
awk '...' f Constant (llevat d'arrays) Molt alta Filtrar, comptar, agregar, calcular: gairebé tot el que fa un informe

La regla pràctica, en una frase: si dins del while només filtres, comptes o sumes, el teu bucle és un awk mal escrit. El while read es justifica quan cada línia dispara accions que awk no sap fer bé —llançar un curl per enviament, invocar funcions del shell, escriure en fitxers diferents—; i tot i així convé preguntar-se si awk pot generar la llista i el bucle limitar-se a actuar-hi.

  1. Paral·lelitzar de debò, i quan no serveix

Quan la feina és independent per element i cadascun triga de debò —consultar l'API per cada ciutat, comprimir cada rotat—, la paral·lelització és la millora més gran disponible. Amb xargs -P (05-01) o amb &/wait (05-02):

# 8 processos en paral·lel, un per fitxer, amb noms segurs (-0)
find /var/log/veloz -name '*.log.1' -print0 \
    | xargs -0 -P 8 -n 1 gzip -9

# Versio amb & i wait, quan cal logica de shell per element
for ciutat in Valencia Sevilla Bilbao Madrid; do
    veloz_api_get "/envios?ciudad=$ciutat" > "$tmp/$ciutat.json" &
done
wait

Dues advertències imprescindibles. La primera: si el coll d'ampolla és el disc, paral·lelitzar empitjora les coses. Vuit processos llegint alhora d'un disc mecànic generen cerques aleatòries i poden trigar més que un de sol; en SSD l'efecte és menor però el límit hi és igualment. Mira el time: si real és alt però user + sys és baix, estàs esperant E/S i més processos no t'ajudaran. La segona: els processos paral·lels escriuen entremesclat; que cadascun escrigui al seu propi fitxer i es consolidi al final, com feia flota.sh a 07-06. Un punt de partida raonable per al grau de paral·lelisme és -P "$(nproc)" en tasques de CPU (06-03), i força menys en tasques de disc.

  1. LC_ALL=C, memòria i el criteri final

Un accelerador que gairebé ningú no coneix: la configuració regional. Amb ca_ES.UTF-8, sort i grep apliquen regles d'ordenació i classes de caràcters multibyte; amb LC_ALL=C comparen byte a byte.

$ time sort enviaments.csv > /dev/null              # real 0m9,84s
$ time LC_ALL=C sort enviaments.csv > /dev/null     # real 0m2,31s

Quatre vegades més ràpid per una variable. La condició és que l'ordre byte a byte et serveixi: per a claus internes, identificadors o camins, sí; per a una llista de ciutats que veurà un humà, no, perquè Àvila acabarà fora de lloc. Al toolkit s'aplica als ordenaments intermedis i es deixa la configuració regional a la presentació final.

Sobre la memòria, una regla simple: no carreguis en memòria el que pots recórrer en flux. mapfile -t linies < /var/log/veloz/acces.log amb un registre d'un gigabyte intenta reservar més d'un gigabyte de RAM —els arrays de Bash tenen força sobrecàrrega per element— i pot acabar amb el procés mort pel gestor de memòria del nucli, de matinada i sense explicació. Els fluxos (while read, canonades, awk) fan servir memòria constant independentment de la mida del fitxer.

I el criteri final, el més important de tots: saber quan el problema ja no és de Bash. Senyals concrets que has arribat al límit:

  • Després d'aplicar tot l'anterior, l'script continua trigant minuts.
  • El gruix de la lògica ja és dins d'un awk de quaranta línies amb la seva pròpia estructura de dades.
  • Necessites estructures que Bash no té: nombres en coma flotant de debò, arrays de dues dimensions, JSON imbricat que jq ja no cobreix amb comoditat.
  • Necessites creuar dos fitxers grans per una clau, ordenar per diversos criteris o consultar històric: això és una base de dades, encara que sigui SQLite.
  • L'script passa de 500 línies i la seva lògica de negoci importa més que la seva lògica de sistema.

Bash és excel·lent enganxant ordres del sistema; és mal llenguatge per a càlcul intensiu. Reconèixer-ho a temps és una decisió tècnica, no una derrota: informe-diari.sh continua sent Bash, però si demà cal creuar enviaments amb facturació i històric de tres anys, aquest tros s'escriu en Python o en SQL i Bash l'orquestra.

  1. Aplicació: perfilar informe-diari.sh

Generem un CSV representatiu i mesurem la línia base:

$ awk 'BEGIN { print "id_enviament,data,ciutat,repartidor,estat,import"
    srand(); c["1"]="Valencia"; c["2"]="Sevilla"; c["3"]="Bilbao"; c["4"]="Madrid"
    e["1"]="lliurat"; e["2"]="en_repartiment"; e["3"]="incidencia"
    for (i = 1; i <= 500000; i++)
        printf "E%06d,2026-08-03,%s,alopez,%s,%.2f\n", i,
               c[int(rand()*4)+1], e[int(rand()*3)+1], rand()*90+5
}' > /tmp/enviaments-gran.csv

$ time ./informe-diari.sh -f /tmp/enviaments-gran.csv > /dev/null
real    3m12,4s      user 1m28,1s      sys 1m36,8s

sys gairebé igual que user: el diagnòstic de l'apartat 3 és immediat, això són processos. Instrumentem l'script amb SECONDS al voltant de cada bloc per localitzar el punt calent:

lectura i recompte ........ 188 s   <-- aqui hi es tot
top de repartidors .........  3 s
generacio de l'HTML ........  1 s
enviament del correu .......  2 s

Dins del bloc de 188 segons hi havia un while IFS=, read amb un cut i un date -d per línia (un milió de processos) més quatre grep -c posteriors sobre el fitxer complet. Els tres canvis aplicats, en aquest ordre i mesurant-ne cadascun:

Canvi real Acumulat
Línia base 3 min 12 s
Treure cut/date del bucle (IFS=, + printf -v) 41 s ×4,7
Substituir el bucle i els quatre grep -c per un awk d'una passada 4,8 s ×40
LC_ALL=C al sort del top de repartidors 3,9 s ×49

De 192 segons a 3,9: quaranta-nou vegades més ràpid sense canviar ni una línia de la lògica de negoci, només on s'executa cada cosa. I aquí s'aplica el pas 4 del mètode: parem. Queden 3,9 segons, dels quals 2 són l'enviament del correu, que depèn de la xarxa. Continuar optimitzant un script que s'executa un cop al dia a les 06:30 no aporta res a ningú.

Errors Habituals i Consells

  • Optimitzar sense línia base. Sense el número de partida no pots demostrar la millora ni detectar que has empitjorat les coses. Anota el time inicial abans de tocar res.
  • Mesurar un sol cop. La primera execució paga la memòria cau de disc freda. Executa tres cops i fes servir la mediana; per comparar de debò, hyperfine automatitza justament això.
  • Micro-optimitzar fora dels bucles. Canviar basename per ${f##*/} en una línia que s'executa un cop no estalvia res i pot restar claredat. L'interior dels bucles és on es guanya.
  • Confondre «menys línies» amb «més ràpid». Una canonada de sis etapes és curta d'escriure i crea sis processos; un awk de tres línies en crea un.
  • Paral·lelitzar una feina limitada pel disc. Si real és alt però la CPU està ociosa, més processos només afegeixen contenció.
  • Consell: set -x amb PS4 fa de perfilador pobre. Amb PS4='+ $(date +%s.%N) ' cada línia de traça (05-03) queda marcada amb el seu instant i veus els salts de temps.
  • Consell: l'optimització més rendible sol ser no fer la feina. Abans d'accelerar el processament de tot l'històric, pregunta't si cal processar més que el dia en curs.

Exercicis

Exercici 1. Aquest bloc triga 2 minuts amb 300.000 línies. Identifica els tres problemes de rendiment i reescriu-lo en una sola passada.

total=0; suma=0
for id in $(cut -d, -f1 enviaments.csv | tail -n +2); do
    linia=$(grep "^$id," enviaments.csv)
    import=$(echo "$linia" | cut -d, -f6)
    total=$(expr $total + 1)
    suma=$(echo "$suma + $import" | bc)
done
echo "$total enviaments, $suma euros"

Exercici 2. Escriu una funció veloz_mesura que executi una ordre tres cops, mostri el temps real de cada execució en mil·lisegons i la mediana, fent servir date +%s%N.

Exercici 3. Un script triga real 5m10s, user 0m12s, sys 0m8s. On és el temps i quina optimització té sentit? I si fos real 5m10s, user 2m40s, sys 2m20s, què buscaries?

Solucions

Solució 1.

read -r total suma < <(awk -F, 'NR > 1 { total++; suma += $6 }
                                END { printf "%d %.2f\n", total, suma }' enviaments.csv)
printf '%d enviaments, %.2f euros\n' "$total" "$suma"

Els tres problemes. (1) Complexitat quadràtica: per cadascun dels 300.000 identificadors es fa un grep que recorre el fitxer sencer; són 9×10¹⁰ comparacions de línia. És el defecte greu, i desapareix en recórrer el fitxer un cop en lloc de buscar-hi a dins. (2) Processos dins del bucle: grep, echo, cut, expr i bc són cinc processos per volta, un milió i mig en total. (3) for sobre una substitució d'ordres, que a més de crear la subshell construeix en memòria una cadena amb els 300.000 identificadors i la parteix pels espais. La versió d'awk fa una passada, un procés i suma en coma flotant de manera nativa, sense bc (06-01).

Solució 2.

# veloz_mesura <ordre...>
# Executa l'ordre 3 cops descartant la seva sortida i mostra els temps i la mediana.
veloz_mesura() {
    local -a temps=(); local i inici
    for i in 1 2 3; do
        inici=$(date +%s%N)
        "$@" >/dev/null 2>&1
        temps+=( $(( ($(date +%s%N) - inici) / 1000000 )) )
        printf '  execucio %d: %d ms\n' "$i" "${temps[-1]}"
    done
    mapfile -t ordenats < <(printf '%s\n' "${temps[@]}" | sort -n)
    printf 'mediana: %d ms\n' "${ordenats[1]}"
}

Es fa servir "$@" sense cometes de més perquè l'ordre i els seus arguments arribin intactes (03-05), la sortida es descarta per no mesurar el terminal, i la mediana de tres és l'element central de l'array ordenat, que és més robusta que la mitjana davant d'una execució anòmala.

Solució 3. En el primer cas el procés només consumeix 20 segons de CPU dels 310 de rellotge: està esperant, gairebé segur xarxa o disc. Optimitzar el codi no canviarà res; cal buscar l'espera (un curl sense --max-time, un sleep heretat, un ssh sense ControlMaster, una lectura sobre un muntatge de xarxa) i atacar-la amb paral·lelisme, memòria cau o límits de temps. En el segon cas la CPU sí que està ocupada, i el repartiment gairebé a parts iguals entre user i sys delata creació massiva de processos: cal buscar ordres externes dins de bucles i convertir-les en expansions o en un awk d'una passada.

Conclusió

Optimitzar en Bash comença i acaba al mateix lloc: mesurar. time dóna les tres xifres que orienten tota la feina —real és el que pateix l'usuari, user és càlcul i sys és nucli, així que un sys alt significa processos i un real molt superior a user + sys significa espera—; SECONDS acota blocs llargs dins de l'script i date +%s%N blocs curts, sempre repetint la mesura i quedant-se amb la mediana sobre dades representatives. El cost dominant és crear processos: 1-3 ms cadascun, insignificants sols i demolidors multiplicats per mig milió de línies, com demostra el salt de 21 minuts a 12 segons només traient echo i cut de dins del bucle, i d'allà a 0,9 s amb un únic awk. D'aquí se'n deriven les regles pràctiques: fes servir builtins a l'interior dels bucles ($(( )) en comptes d'expr, ${f##*/} en comptes de basename, ${#s} en comptes de wc -c) i no t'hi amoïnis fora; elimina les etapes inútils de les canonades (grep -c en comptes de grep | wc -l, awk que filtra en comptes de grep | awk); llegeix el fitxer una sola vegada calculant tots els agregats en una passada; evita subshells innecessàries, agrupant amb { } en comptes de ( ) i fent servir done < <(cmd); i redirigeix el bucle sencer en lloc d'acumular >>. Entre while read, mapfile i awk, la regla és que si dins del bucle només filtres, comptes o sumes, aquest bucle hauria de ser un awk; mapfile només per a fitxers petits que cal recórrer diverses vegades. Paral·lelitzar amb xargs -P o &/wait és el guany més gran quan la feina és independent i pesada, però no ajuda si el coll d'ampolla és el disc. LC_ALL=C multiplica per quatre la velocitat de sort quan l'ordre byte a byte serveix, i cap registre d'un gigabyte no ha d'entrar en un array. El cas real tanca l'argument: informe-diari.sh va passar de 3 min 12 s a 3,9 s —quaranta-nou vegades— sense tocar la lògica de negoci, i llavors vam parar, perquè optimitzar més un script que corre un cop al dia no li serveix a ningú.

El toolkit ja és llegible i ràpid. Queda la pregunta més incòmoda de les tres que obrien el mòdul: és segur? Aquests scripts executen tasques privilegiades, guarden claus SSH, llegeixen fitxers de configuració amb credencials i processen dades personals de clients. La lliçó 08-03 els audita de dalt a baix: injecció d'ordres i per què eval és una bomba, validació d'entrada amb llista blanca, gestió de secrets, fitxers temporals segurs, mínim privilegi, condicions de cursa i tractament de dades personals, amb una llista de comprovació aplicada al toolkit complet.

Curs de Programació en Bash

Mòdul 1: Introducció a Bash

Mòdul 2: Ordres Bàsiques de Bash

Mòdul 3: Fonaments de Scripting

Mòdul 4: Scripting Intermedi

Mòdul 5: Tècniques Avançades de Scripting

Mòdul 6: Treballar amb Eines Externes

Mòdul 7: Automatització i Programació

Mòdul 8: Bones Pràctiques i Optimització

Mòdul 9: Projectes del Món Real

© Copyright 2026. Tots els drets reservats