El toolkit deixa d'esperar les teves ordres. Fins ara, informe-diari.sh només existia si algú es recordava de llançar-lo i estat-servei.sh s'executava quan ja sospitaves que alguna cosa anava malament —és a dir, tard—. cron és el dimoni que fa des dels anys setanta que resol exactament això: un procés que es desperta cada minut, mira una taula d'horaris i executa el que toca. És simple, hi és a tot arreu i funciona. Però té una particularitat que fa ensopegar tothom el primer cop: l'entorn en què executa els teus scripts no s'assembla al del teu terminal. Aquesta lliçó cobreix el model de cron, la seva sintaxi camp a camp i, sobretot, com depurar el clàssic «al meu terminal funciona».
Contingut
- El model de
croni on viuen les tasques crontab -l,-e,-ri el perill del teclat- La sintaxi dels cinc camps
- Llistes, rangs, passos i dreceres
- L'entorn mínim de cron
- El
%,MAILTOi la captura de la sortida - Depurar una tasca de cron
- Encavalcament, permisos i zona horària
- Parents de cron:
at,anacroni els temporitzadors - Aplicació: el toolkit al crontab
- El model de
cron i on viuen les tasques
cron i on viuen les tasquescron és un dimoni: arrenca amb el sistema i es queda viu. El seu cicle és senzill: es desperta al principi de cada minut, llegeix les taules de tasques (crontabs), llança les ordres l'especificació horària de les quals coincideix amb aquell minut i se'n torna a dormir. D'aquí en surten dues conseqüències. La primera: la granularitat mínima és el minut; «cada 30 segons» no existeix a cron (per a això hi ha els temporitzadors de 07-05). La segona: cron no espera que l'execució anterior acabi, així que les tasques es poden encavalcar (apartat 8).
Comprova-ho amb allò de 06-03: systemctl is-active cron ha de retornar active (crond a la família Red Hat). Si el dimoni no corre, les teves tasques no s'executen i ningú no t'avisa. Ara la primera confusió: no hi ha un crontab, hi ha diversos llocs i no comparteixen format.
| Ubicació | Qui l'edita | Camp d'usuari? | Ús típic |
|---|---|---|---|
crontab -e (d'usuari) |
Cada usuari, el seu | No | Tasques d'un servei que corre amb el teu usuari |
/etc/crontab |
root, a mà |
Sí | Tasques del sistema (avui poc usat) |
/etc/cron.d/fitxer |
root, un fitxer per paquet |
Sí | El que instal·len els paquets i el que desplegues tu |
/etc/cron.{hourly,daily,weekly,monthly}/ |
root |
No aplica | Scripts executables solts, sense horari propi |
La diferència clau és a la columna del mig: els crontabs del sistema porten un camp extra amb l'usuari, just després dels cinc de temps. Els d'usuari no, perquè ja se sap qui és.
30 6 * * * /home/veloz/veloz-ops/bin/informe-diari.sh # crontab d'usuari
30 6 * * * veloz /home/veloz/veloz-ops/bin/informe-diari.sh # /etc/cron.d/velozPosar una línia d'usuari a /etc/cron.d/ és un error habitual: cron interpretarà /home/veloz/... com el nom de l'usuari i fallarà. Els directoris cron.daily i companyia són un altre cas: allà no hi ha horaris, hi ha scripts executables (sense extensió .sh i amb permís d'execució) que el sistema llança un cop al dia, a l'hora que decideixi /etc/crontab o anacron.
crontab -l, -e, -r i el perill del teclat
crontab -l, -e, -r i el perill del teclatEl teu crontab personal es gestiona sempre amb l'ordre crontab, mai editant fitxers a mà.
| Ordre | Què fa |
|---|---|
crontab -l |
Llista el teu crontab per la sortida estàndard |
crontab -e |
L'obre a $EDITOR i el valida en desar |
crontab -r |
L'esborra sencer, sense preguntar |
crontab fitxer |
Substitueix el teu crontab pel contingut d'aquell fitxer |
crontab -u veloz -l |
El d'un altre usuari (requereix root) |
-l i -r són tecles contigües en QWERTY, i no hi ha paperera. Dos hàbits eviten el desastre:
crontab -l > ~/veloz-ops/etc/crontab.bak # copia abans de tocar res
crontab ~/veloz-ops/etc/crontab.veloz # instal·lar des de fitxer versionatLa segona línia és la bona pràctica de debò: mantén el crontab com un fitxer de text al teu repositori (Git a 08-04) i instal·la'l des d'allà. Es desa realment a /var/spool/cron/crontabs/<usuari> (Debian/Ubuntu), però no l'editis allà: crontab -e a més valida la sintaxi i avisa el dimoni.
- La sintaxi dels cinc camps
Cada línia comença amb cinc camps separats per espais, de la unitat més petita a la més gran:
minut(0-59) hora(0-23) diaMes(1-31) mes(1-12 o jan-dec) diaSetmana(0-7, 0 i 7 = diumenge)
30 6 * * * /cami/a/l/ordreL'asterisc significa «qualsevol valor», així que 30 6 * * * es llegeix: minut 30, hora 6, qualsevol dia, qualsevol mes, qualsevol dia de la setmana → cada dia a les 06:30. Un detall que sorprèn: quan dia del mes i dia de setmana estan tots dos restringits (cap dels dos és *), cron els combina amb un O lògic. 0 0 13 * 5 no és «els divendres 13», és «tots els dies 13 i a més tots els divendres»; per a «divendres 13» cal posar la condició dins de l'ordre.
- Llistes, rangs, passos i dreceres
Cada camp admet quatre construccions, barrejables:
| Construcció | Sintaxi | Al camp del minut |
|---|---|---|
| Valor | 15 |
Només el minut 15 |
| Llista | 1,15,45 |
Els minuts 1, 15 i 45 |
| Rang | 10-20 |
Del 10 al 20, tots |
| Pas | */10 |
0, 10, 20, 30, 40, 50 |
| Pas sobre rang | 0-30/5 |
0, 5, 10, 15, 20, 25, 30 |
| Combinació | 0,30,45-50 |
0, 30, 45, 46, 47, 48, 49, 50 |
*/N no significa «cada N minuts a partir d'ara», sinó «els valors divisibles entre N». Per això */7 dona 0, 7, … 56 i després salta a 0: entre dues execucions a cavall de l'hora només passen 4 minuts. Amb divisors de 60 (2, 3, 5, 10, 15, 20, 30) el repartiment és exacte.
| Especificació | Quan s'executa |
|---|---|
*/10 * * * * |
Cada 10 minuts |
0 * * * * |
En punt, cada hora |
30 6 * * * |
Cada dia a les 06:30 |
0 3 * * 0 |
Els diumenges a les 03:00 |
0 9 * * 1-5 |
De dilluns a divendres a les 09:00 |
*/15 8-20 * * 1-5 |
Cada 15 min, de 8 a 20 h, feiners |
0 2 1 * * |
El dia 1 de cada mes a les 02:00 |
0 0 1 1,4,7,10 * |
El primer dia de cada trimestre |
Existeixen a més dreceres: @yearly (0 0 1 1 *), @monthly (0 0 1 * *), @weekly (0 0 * * 0), @daily (0 0 * * *), @hourly (0 * * * *) i @reboot, que s'executa en arrencar cron. Són llegibles, però totes disparen a la mateixa hora: si deu servidors fan @daily, els deu arrenquen a les 00:00 clavades i competeixen per xarxa i disc. En producció és millor 17 3 * * * amb una hora triada. I @reboot es dispara quan arrenca el dimoni, no quan el sistema està a punt: si el teu script necessita la xarxa, pot arribar massa aviat (ho resolen les dependències de systemd, 07-05).
- L'entorn mínim de cron
Aquest és **l'**apartat de la lliçó: quan un script funciona al teu terminal i falla a cron, la resposta gairebé sempre és aquí.
Cron no obre un shell d'inici de sessió ni interactiu. Repassa 01-04: no es llegeix ~/.bashrc, ni ~/.bash_profile, ni /etc/profile.
| Variable | Al teu terminal | A cron |
|---|---|---|
PATH |
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:… |
/usr/bin:/bin |
SHELL |
/bin/bash |
/bin/sh |
HOME |
/home/veloz |
/home/veloz (sí que hi és) |
LANG, LC_* |
La teva configuració regional | Sense definir |
| Tota la resta | El que hi posi el teu .bashrc |
Res |
Tres conseqüències pràctiques: un jq instal·lat a /usr/local/bin no es troba; un àlies o funció del teu .bashrc no existeix; i SHELL=/bin/sh significa que la línia del crontab s'interpreta amb sh, sense [[ ]], arrays ni <( ) (dins del teu script sí que els tens, perquè mana el seu shebang). L'experiment més útil que pots fer és programar * * * * * env > /tmp/entorn-cron.txt i comparar-ho amb el teu env.
Hi ha tres solucions, les tres legítimes:
# 1. Camins absoluts a tot arreu (la mes robusta)
30 6 * * * /home/veloz/veloz-ops/bin/informe-diari.sh
# 2. Definir l'entorn al mateix crontab, a dalt de tot
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
30 6 * * * informe-diari.sh
# 3. Carregar la configuracio explicitament abans d'executar
30 6 * * * . /home/veloz/veloz-ops/etc/veloz-ops.conf && /home/veloz/veloz-ops/bin/informe-diari.shLes assignacions del crontab (opció 2) són variables d'entorn normals, s'apliquen a totes les tasques d'aquella taula i no admeten expansió: PATH=$PATH:/opt/bin no fa el que esperes, cal escriure el valor complet.
- El
%, MAILTO i la captura de la sortida
%, MAILTO i la captura de la sortidaDins d'una línia de crontab, el primer % marca el final de l'ordre i tot el que ve al darrere s'envia com a entrada estàndard; els següents es converteixen en salts de línia. És un parany perfecte perquè % apareix a totes les cadenes de format de date:
0 2 * * * tar czf /copies/dades-$(date +%Y-%m-%d).tar.gz /srv/veloz/dades # MALAMENT: es talla
0 2 * * * tar czf /copies/dades-$(date +\%Y-\%m-\%d).tar.gz /srv/veloz/dades # BÉ: escapatLa manera de no haver de recordar mai aquesta regla és no posar lògica al crontab: una línia hauria de ser un camí absolut, les seves opcions i una redirecció; res més.
Sobre la sortida: si la tasca escriu alguna cosa per stdout o stderr, cron intenta enviar-t'ho per correu local. En un servidor sense agent de correu, aquell missatge es perd i els teus errors desapareixen. [email protected] fixa el destinatari i MAILTO="" desactiva el correu. El patró correcte en un servidor modern és un registre propi:
30 6 * * * /home/veloz/veloz-ops/bin/informe-diari.sh >> /home/veloz/veloz-ops/logs/informe.log 2>&1Repassa 02-04: >> afegeix sense truncar i 2>&1 després porta també els errors al mateix fitxer. I la mala pràctica que veuràs a tot arreu: >/dev/null 2>&1 a cegues silencia la tasca per complet, així que el dia que falli no hi haurà correu, ni registre, ni rastre. Només és acceptable quan l'script ja escriu el seu propi registre (07-04), i tot i així convé deixar 2>> a un fitxer d'errors.
- Depurar una tasca de cron
Quan alguna cosa programada no funciona, segueix aquest ordre.
S'ha intentat executar? Cron registra cada llançament al registre del sistema:
Si aquella línia no apareix, el problema és l'especificació horària o el crontab, no el teu script; si apareix, és a dins.
Reprodueix l'entorn. env -i el neteja per complet i et deixa executar com ho faria cron:
Si aquí falla i al teu terminal no, ja tens el diagnòstic: depens d'alguna cosa de l'entorn, normalment un binari a /usr/local/bin. Després, mira el registre de la tasca (si no el vas redirigir, fes-ho ara) i, si cal, puja el detall amb set -x i el PS4 personalitzat de 05-03, o cridant l'script amb bash -x.
- Encavalcament, permisos i zona horària
Encavalcament. Cron no comprova si l'execució anterior segueix viva. Programa estat-servei.sh cada 10 minuts, deixa que l'API es pengi 40, i tindràs quatre processos barallant-se pel mateix registre. La solució la coneixes des de 05-02:
*/10 * * * * /usr/bin/flock -n /var/lock/veloz-estat.lock /home/veloz/veloz-ops/bin/estat-servei.sh >> /cami/estat.log 2>&1-n significa «si el pany està pres, surt immediatament», així l'execució encavalcada es descarta en lloc d'acumular-se. A 07-02 en veuràs la versió completa, amb el pany dins del mateix script.
Permisos. Si existeix /etc/cron.allow, només els usuaris llistats poden tenir crontab; si no existeix però sí /etc/cron.deny, poden tots menys els llistats. Un «you are not allowed to use this program» s'explica sempre per aquests dos fitxers.
Zona horària i canvi d'hora. Cron fa servir la del sistema (timedatectl), així que la mateixa línia s'executa a hores diferents en servidors de zones diferents: la pràctica habitual és posar-los tots en UTC. I a la primavera, quan el rellotge salta de les 02:00 a les 03:00, una tasca a les 02:30 no s'executa aquell dia; a la tardor es pot executar dues vegades. Regla pràctica: no programis res entre les 02:00 i les 03:00 si t'importa que passi exactament un cop —i si la tasca és idempotent (07-02), això deixa de ser un problema—.
- Parents de cron:
at, anacron i els temporitzadors
at, anacron i els temporitzadorsat executa una ordre un sol cop, en un moment concret: echo '/cami/copia.sh' | at 23:00, amb at -l per llistar i atrm N per cancel·lar. És ideal per a «llança això quan acabi la finestra de manteniment» sense deixar res permanent. anacron, per la seva banda, resol un problema de disseny de cron: si la màquina està apagada a l'hora prevista, l'execució es perd. Anacron no treballa amb hores sinó amb períodes en dies i, en arrencar, comprova quant fa que es va executar cada tasca. Per això és el normal en portàtils, i per això en molts sistemes els directoris cron.daily els dispara realment anacron.
| Eina | Repetitiva | Recupera el que s'ha perdut | Granularitat | Complexitat |
|---|---|---|---|---|
cron |
Sí | No | Minut | Molt baixa |
anacron |
Sí | Sí | Dia | Baixa |
at |
No (un cop) | No | Minut | Molt baixa |
| Temporitzador de systemd | Sí | Sí (Persistent=true) |
Segon | Mitjana |
Els temporitzadors cobreixen tot l'anterior i hi afegeixen dependències, registre centralitzat i aïllament, a canvi de més fitxers. Són el tema de 07-05.
- Aplicació: el toolkit al crontab
Aquest és el crontab de srv-veloz-01, desat com a fitxer versionat a ~/veloz-ops/etc/crontab.veloz:
# Crontab d'operacions de Veloz Envios — srv-veloz-01
# Instal·lar amb: crontab ~/veloz-ops/etc/crontab.veloz
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
VELOZ_OPS=/home/veloz/veloz-ops
# Informe diari del CSV d'enviaments — cada dia a les 06:30
30 6 * * * $VELOZ_OPS/bin/informe-diari.sh >> $VELOZ_OPS/logs/informe.log 2>&1
# Estat de la veloz-api — cada 10 minuts, sense encavalcar-se
*/10 * * * * /usr/bin/flock -n /var/lock/veloz-estat.lock $VELOZ_OPS/bin/estat-servei.sh >> $VELOZ_OPS/logs/estat.log 2>&1Cada decisió respon a alguna cosa d'aquesta lliçó. SHELL=/bin/bash perquè les línies s'interpretin amb Bash. PATH complet perquè jq viu a /usr/local/bin. MAILTO="" perquè el servidor no té correu i ja redirigim a un registre. VELOZ_OPS com a variable pròpia, perquè cron sí que expandeix dins de les ordres les variables definides al crontab. 30 6 i no @daily: hora triada, sense competir amb tota la resta a mitjanit. */10 perquè 10 és divisor de 60 i el repartiment és exacte. flock -n només al segon, que és el que pot trigar més que el seu interval. >> … 2>&1 a tots dos, mai /dev/null. I cap lògica ni % a les línies: dates i decisions viuen dins dels scripts.
S'instal·la amb crontab -l > ~/veloz-ops/etc/crontab.bak (xarxa de seguretat) seguit de crontab ~/veloz-ops/etc/crontab.veloz, i l'endemà es verifica amb journalctl -u cron --since "06:00". Aquests dos registres creixeran sense parar: la rotació amb logrotate es resol a 07-04.
Errors Habituals i Consells
- Camins relatius. Cron executa des de
$HOMEamb unPATHmínim:./informe-diari.shno funciona. Camí absolut sempre. - Suposar que es llegeix
.bashrc. No es llegeix: ni àlies, ni funcions, ni elPATHque hi vas afegir. - Oblidar
2>&1, o usar>/dev/null 2>&1per costum. El primer deixa els errors fora del registre; el segon silencia l'únic avís que anaves a rebre. - El
%sense escapar. Talla l'ordre en sec. Millor: no posisdateal crontab. - Confondre crontab d'usuari amb
/etc/cron.d/. El segon porta camp d'usuari; el primer no. - Falta el salt de línia final, o l'script no és executable. Algunes versions ignoren l'última línia sense
\n; i sensechmod +x(02-03) cal invocar-lo com/bin/bash /cami/script.sh. crontab -ren lloc de-l. Copia abans de tocar res i mantén el crontab en un fitxer versionat.- Consell: quan estrenis una tasca, programa-la primer cada 2 minuts amb sortida a un registre, comprova que funciona i només llavors posa-li el seu horari definitiu. Esperar 24 hores per descobrir un error de camí és temps perdut.
Exercicis
Exercici 1. Tradueix a sintaxi de cron: (a) cada 5 minuts entre les 07:00 i les 21:59 de dilluns a dissabte; (b) el primer dia de cada mes a les 04:15; (c) cada mitja hora tots els dies; (d) els dilluns, dimecres i divendres a les 22:00.
Exercici 2. Aquesta línia «no funciona». Troba els quatre problemes i reescriu-la:
Exercici 3. Escriu una funció veloz_cron_instala per a lib/comu.sh que instal·li un fitxer de crontab de manera segura: desa còpia de l'actual, valida que el nou existeix i no està buit, l'instal·la i verifica que hi ha quedat instal·lat, restaurant la còpia si alguna cosa falla.
Solucions
Solució 1.
| Cas | Especificació | Explicació |
|---|---|---|
| (a) | */5 7-21 * * 1-6 |
El rang 7-21 cobreix fins a les 21:59 perquè inclou tota l'hora 21 |
| (b) | 15 4 1 * * |
Minut 15, hora 4, dia 1 |
| (c) | 0,30 * * * * |
També val */30; la llista explícita es llegeix millor |
| (d) | 0 22 * * 1,3,5 |
Llista al camp de dia de setmana |
L'errada típica a (a) és escriure 7-22 pensant «fins a les 21», cosa que hi afegiria les execucions de 22:00 a 22:55.
Solució 2. Els problemes: (1) ~ no s'expandeix de manera fiable al crontab; (2) % sense escapar, que talla l'ordre a date +; (3) > /dev/null 2>&1 silencia la còpia de seguretat, així que si falla ningú no se n'assabenta; (4) camí relatiu dependent d'un cd que, si falla, deixa el && sense executar-se en silenci.
La data del destí es calcula dins de l'script, que és on ha d'estar: així el crontab no té lògica, no hi ha cap % per escapar i l'script es prova a mà igual que s'executa programat.
Solució 3.
# veloz_cron_instala — instal·la un crontab amb copia de seguretat i verificacio.
veloz_cron_instala() {
local nou="${1:?falta el fitxer de crontab}" copia
[[ -s $nou ]] || { veloz_log_error "el crontab '$nou' no existeix o es buit"; return 66; }
copia=$(mktemp "${TMPDIR:-/tmp}/crontab-copia.XXXXXX") || return 74
crontab -l > "$copia" 2>/dev/null || : # sense crontab previ no es un error
if ! crontab "$nou"; then
veloz_log_error "cron ha rebutjat '$nou'; l anterior segueix intacte"
rm -f "$copia"; return 65
fi
if ! diff -q <(crontab -l) "$nou" >/dev/null; then
veloz_log_error "verificacio fallida; restaurant la copia anterior"
crontab "$copia"; rm -f "$copia"; return 74
fi
veloz_log_info "crontab instal·lat des de '$nou'"
rm -f "$copia"
}Tres decisions mereixen comentari. [[ -s ]] comprova en una sola prova que el fitxer existeix i no està buit (03-04): instal·lar un crontab buit equival a un crontab -r accidental. El || : després de crontab -l neutralitza el codi diferent de zero que retorna quan l'usuari encara no té crontab, cosa que no és una fallada; sense ell, set -e (05-03) avortaria la funció. I la verificació amb diff -q <(crontab -l) fa servir la substitució de procés de 05-05 per comparar el que ha quedat realment instal·lat amb el que volies, en lloc de refiar-se del codi de sortida de crontab.
Conclusió
cron es desperta cada minut, llegeix unes taules i executa el que toca. Les tasques viuen al crontab d'usuari (sense camp d'usuari), a /etc/crontab i /etc/cron.d/ (amb ell) o com a scripts solts a /etc/cron.{hourly,daily,weekly,monthly}. Els cinc camps són minut, hora, dia del mes, mes i dia de setmana, i admeten valors, llistes 1,15, rangs 1-5, passos */10 i combinacions; les dreceres @daily o @reboot són llegibles però concentren la càrrega en hores rodones. Gestiona la taula amb crontab -l, -e i crontab fitxer, amb molt de compte amb -r, que esborra sense preguntar. El que de debò separa una tasca que funciona d'una que no és l'entorn: cron no llegeix ~/.bashrc, dona un PATH de dos directoris i SHELL=/bin/sh, i d'aquí en surten gairebé tots els «al meu terminal funcionava»; s'arregla amb camins absoluts, definint PATH al crontab o carregant la configuració explícitament. Hi afegeixes el % que cal escapar, redirigir sempre amb >> fitxer 2>&1 en lloc de llençar la sortida a /dev/null, embolcallar amb flock -n allò que es pugui encavalcar i depurar en ordre: primer journalctl -u cron per saber si s'ha intentat, després env -i per reproduir l'entorn.
Ja tens l'informe a les 06:30 i la comprovació d'estat cada 10 minuts executant-se soles. Però hi ha una diferència enorme entre programar un script i tenir un script que es pugui executar sense ningú al davant. Què passa si pregunta alguna cosa per teclat i no hi ha teclat? Si s'executa dues vegades per un reintent? Si es queda penjat esperant un servidor que no contesta? Com saps, l'endemà al matí, si va fer la seva feina? A 07-02 convertim això en set propietats concretes —no interactiva, idempotent, exclusiva, observable, acotada, segura davant fallades i configurable— i les implementem una a una sobre informe-diari.sh, inclòs el --dry-run que et deixarà provar una tasca automàtica sense por.
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
