Pubblicato · 9 min di lettura
Espressioni cron da zero: un tutorial visivo
Impara la sintassi cron POSIX a 5 campi, i caratteri speciali, i valori step, le estensioni Quartz, le differenze del cron di AWS e una dozzina di esempi pratici che puoi copiare e incollare in un crontab oggi.
Perché cron è ancora ovunque
Cron fu scritto da Brian Kernighan per la versione 7 di Unix nel 1979. La versione che la maggior parte dei sistemi Linux moderni ancora distribuisce — Vixie cron, scritto da Paul Vixie nel 1987 — aggiunse le variabili d'ambiente, i crontab per utente e la comoda scorciatoia @reboot, ma mantenne il formato originale a cinque campi praticamente intatto. Quella stabilità è l'unico motivo per cui una sintassi progettata prima dell'esistenza del World Wide Web viene ancora incollata nei manifest di Kubernetes, nei workflow di GitHub Actions, nelle regole di AWS EventBridge e nelle annotazioni @Scheduled di Spring nel 2026.
Se impari cron solo copiando righe da Stack Overflow, prima o poi la pianificazione fa qualcosa che non ti aspettavi — il tuo backup giornaliero parte ogni minuto, il tuo job del weekend si attiva il martedì, il fuso orario cambia su un confine DST. Questa guida percorre la sintassi dalle basi, poi mostra abbastanza esempi pratici che potrai riconoscere a colpo d'occhio la forma di qualsiasi espressione.
Il formato a 5 campi
Una riga di crontab POSIX è una pianificazione seguita da un comando. La pianificazione è esattamente cinque campi separati da spazi: minuto, ora, giorno del mese, mese, giorno della settimana. Ogni campo accetta un numero, una lista, un intervallo, uno step o il carattere jolly *.
* * * * * comando-da-eseguire
┬ ┬ ┬ ┬ ┬
│ │ │ │ │
│ │ │ │ └─── giorno della settimana (0-6, domenica = 0; 7 = domenica in molte implementazioni)
│ │ │ └───── mese (1-12, o JAN-DEC)
│ │ └─────── giorno del mese (1-31)
│ └───────── ora (0-23)
└─────────── minuto (0-59)Caratteri speciali: * , - / e affini
Cinque caratteri svolgono quasi tutto il lavoro nel cron classico. Impara cosa significa ciascuno di essi e la maggior parte delle espressioni si leggerà come una frase.
- * — ogni valore in questo campo. * nel campo dei minuti significa "ogni minuto".
- , — lista di valori discreti. 0,15,30,45 nel campo dei minuti significa quattro minuti fissi all'ora.
- - — intervallo inclusivo. 1-5 nel campo del giorno della settimana significa da lunedì a venerdì.
- / — valore di step, scritto come intervallo/step o */step. */10 nei minuti significa "ogni 10 minuti a partire da 0".
- L, W, # — estensioni Quartz, non parte di POSIX. L = ultimo, W = giorno feriale più vicino, # = ennesimo giorno feriale del mese. Maggiori dettagli sotto.
0,30 * * * * a :00 e :30 ogni ora
*/5 * * * * ogni 5 minuti (0, 5, 10, 15 ...)
0 9-17 * * 1-5 ogni ora dalle 09:00 alle 17:00, lun-ven
0 0 1,15 * * mezzanotte il 1° e il 15 di ogni mese
15 14 * * 0 14:15 ogni domenicaI valori step non sono quello che pensi
L'operatore step / lavora sempre su un intervallo, anche quando l'intervallo è implicito. */15 nel campo dei minuti è un'abbreviazione di 0-59/15, che si attiva a 0, 15, 30 e 45. Per questo */7 nel minuto NON significa "ogni 7 minuti da adesso" — significa "i multipli di 7 tra 0 e 59", che ti dà 0, 7, 14, 21, 28, 35, 42, 49, 56 e poi un gap di 4 minuti prima della mezzanotte. Se ti serve un vero intervallo rotante, usa un job runner o un timer systemd con OnUnitActiveSec.
Gli step possono anche iniziare in mezzo a un intervallo. 5-59/10 nei minuti si attiva a 5, 15, 25, 35, 45, 55. Combina intervalli e step quando vuoi una pianificazione non simmetrica senza scrivere una gigantesca lista di virgole.
*/15 * * * * 00, 15, 30, 45 (bene)
*/7 * * * * 00, 07, 14, 21, 28, 35, 42, 49, 56, poi gap di 4 min (probabilmente non è ciò che vuoi)
5-59/10 * * * * 05, 15, 25, 35, 45, 55
0 */4 * * * 00:00, 04:00, 08:00, 12:00, 16:00, 20:00Giorno del mese e giorno della settimana: la regola OR
La più grande trappola di cron: quando sia il giorno del mese sia il giorno della settimana sono limitati (nessuno è *), Vixie cron li tratta come un OR logico, non AND. La riga 0 0 13 * 5 si attiva a mezzanotte del 13 di ogni mese E ogni venerdì — non solo venerdì 13. Per ottenere il comportamento AND, limita solo uno dei due campi e filtra l'altro nel tuo script, oppure usa uno scheduler come Quartz che supporta il placeholder ? per "nessun valore specifico".
0 0 13 * 5 mezzanotte del 13 OPPURE qualsiasi venerdì (regola OR di Vixie cron)
0 0 * * 5 ogni venerdì a mezzanotte
0 0 13 * * ogni 13 del mese a mezzanotte
# Quartz: 0 0 0 13 * 5 ? corrisponderebbe specificamente a venerdì 13Dieci esempi pronti da copiare
Stampa questa tabella e attaccala accanto al monitor. La maggior parte delle righe cron di produzione che scriverai sono variazioni minori di queste.
* * * * * ogni minuto
0 * * * * ogni ora a :00
*/15 * * * * ogni 15 minuti
0 0 * * * ogni giorno a mezzanotte (ora locale del server)
0 9 * * 1-5 giorni feriali alle 09:00
30 14 * * 1-5 giorni feriali alle 14:30
0 9 * * 1 ogni lunedì alle 09:00
0 22 * * 0 ogni domenica alle 22:00
0 0 1 * * mezzanotte del 1° di ogni mese
0 0 1 1 * mezzanotte del 1° gennaio
0 3 * * 6 ogni sabato alle 03:00 (tipica finestra di backup)
*/5 9-17 * * 1-5 ogni 5 minuti durante l'orario d'ufficio, lun-venScorciatoie nominate
Vixie cron, GNU mcron e la maggior parte dei loro discendenti accettano una manciata di alias prefissati con @ che mappano a pianificazioni comuni. Sono più facili da leggere rispetto ai cinque campi equivalenti e più difficili da scrivere male.
@yearly come 0 0 1 1 * (anche @annually)
@monthly come 0 0 1 * *
@weekly come 0 0 * * 0
@daily come 0 0 * * * (anche @midnight)
@hourly come 0 * * * *
@reboot esegui una volta all'avvio del sistemaQuartz: il cugino a 6 e 7 campi
Gli scheduler Java — Quartz stesso, @Scheduled di Spring con cron =, e una manciata di piattaforme cloud — usano un dialetto diverso. Quartz antepone un campo dei secondi e opzionalmente aggiunge un campo dell'anno, dandoti sei o sette campi in totale. Supporta anche L (ultimo), W (giorno feriale più vicino), # (ennesimo giorno feriale del mese) e il placeholder ? che significa "nessun valore specifico, solo uno tra giorno del mese e giorno della settimana è impostato".
Se copi un'espressione Quartz in un crontab Linux, sarà silenziosamente interpretata male, perché lo 0 iniziale sembra un normale valore di minuto a Vixie cron. Sappi sempre quale dialetto si aspetta il tuo scheduler prima di salvare il file.
Campi Quartz: secondo minuto ora giorno-del-mese mese giorno-della-settimana [anno]
0 0 12 * * ? ogni giorno a mezzogiorno
0 15 10 ? * MON-FRI giorni feriali alle 10:15
0 0 0 L * ? ultimo giorno di ogni mese a mezzanotte
0 0 0 LW * ? ultimo giorno feriale di ogni mese
0 0 9 ? * 2#1 primo lunedì di ogni mese alle 09:00
0 0 9 ? * 6#3 terzo venerdì di ogni mese alle 09:00
0 0 0 1 * ? 2026 mezzanotte del 1° di ogni mese, ma solo nel 2026Espressioni rate vs cron di AWS
EventBridge, CloudWatch Events e Lambda usano una sintassi cron simile a Quartz con due deviazioni importanti. Non c'è il campo dei secondi (sei campi, non sette) e devi usare ? in uno tra giorno del mese o giorno della settimana — mai entrambi come *. AWS offre anche rate(valore unità) per intervalli semplici, che evita del tutto la trappola dell'OR.
Campi cron AWS: minuto ora giorno-del-mese mese giorno-della-settimana anno
cron(0 12 * * ? *) ogni giorno alle 12:00 UTC
cron(0/15 * * * ? *) ogni 15 minuti
cron(0 9 ? * MON-FRI *) giorni feriali alle 09:00 UTC
cron(0 0 1 * ? *) mezzanotte UTC del 1° di ogni mese
rate(5 minutes) ogni 5 minuti
rate(1 hour) ogni ora
rate(7 days) ogni 7 giorni a partire dalla creazione della regolaFusi orari, DST e le cose che si rompono alle 02:30
Vixie cron rispetta il fuso orario del sistema (variabile d'ambiente TZ, o ciò a cui punta /etc/localtime). Quando l'ora legale salta avanti di un'ora, qualsiasi job pianificato dentro quel gap viene silenziosamente saltato. Quando il DST torna indietro, i job pianificati dentro l'ora duplicata vengono eseguiti due volte in alcune implementazioni e una volta in altre. Le due impostazioni predefinite sicure: esegui cron in UTC, oppure pianifica fuori dalla finestra 01:00-04:00.
Gli scheduler cloud si comportano diversamente. EventBridge gira sempre in UTC. I CronJob di Kubernetes rispettano il campo spec.timeZone aggiunto in v1.27. Quartz usa il default della JVM a meno che non passi un CronTrigger con un fuso orario. Leggi sempre la documentazione almeno una volta per qualsiasi scheduler stai configurando; non dare per scontato che la convenzione dell'ultimo si trasferisca.
Debug di un'espressione che non si attiva
Nove volte su dieci l'espressione in sé va bene e l'ambiente è sbagliato. Scorri questa checklist prima di incolpare la pianificazione.
- Leggi il log del daemon cron: grep CRON /var/log/syslog su Debian/Ubuntu, journalctl -u cron su host systemd. Il log mostra la riga di comando risolta che cron ha effettivamente eseguito.
- Conferma l'utente. crontab -l senza flag mostra il TUO crontab; la pianificazione che ti interessa potrebbe essere in /etc/crontab, /etc/cron.d/*, o nel crontab di un altro utente.
- Controlla il PATH. Cron gira con un PATH minimo (di solito /usr/bin:/bin). Usa percorsi assoluti o imposta PATH= all'inizio del crontab.
- Controlla la shell. Cron usa /bin/sh, non la tua shell di login. Qualsiasi cosa specifica di bash (process substitution, [[ )) richiede un wrapper esplicito bash -c.
- Reindirizza l'output. Senza > /var/log/myjob.log 2>&1 un job fallito invia email a root localmente e tu non lo vedi mai.
- Conferma il fuso orario. date && date -u dentro il job logga entrambi. Se non corrispondono a ciò che la tua pianificazione presumeva, quello è il tuo bug.
# Una riga cron più sicura:
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
*/5 * * * * /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1Quando cron è lo strumento sbagliato
Cron è eccellente per pianificazioni a orologio fisso e imbattibile per job di una riga su un singolo host. È una scelta inadeguata quando hai bisogno di: coordinamento distribuito su molte macchine (usa una coda, uno scheduler con elezione del leader, o i CronJob di Kubernetes con concurrencyPolicy: Forbid); retry e back-off in caso di fallimento (avvolgi lo script o usa un workflow engine); granularità sotto il minuto (usa un timer systemd o un trigger event-driven); o pianificazioni che dipendono dal completamento dell'esecuzione precedente (cron avvierà felicemente una seconda copia mentre la prima è ancora in esecuzione). Riconoscere il limite presto risparmia molte chiamate alle 3 del mattino.
Costruisci e verifica visivamente
Se scrivi più di un paio di righe cron al mese, l'abitudine con la massima leva è controllare ogni espressione con un parser prima di committarla. Multilities distribuisce un /tools/cron-builder gratuito che decodifica un'espressione a 5 o 6 campi in italiano semplice e mostra le prossime dieci ore di attivazione nel tuo fuso orario locale, che è esattamente il controllo incrociato che cattura la trappola dell'OR giorno-del-mese/giorno-della-settimana e la sorpresa dello step in stile */7. Abbinalo a un rapido test unitario che verifichi il prossimo orario di attivazione per una data di ancoraggio nota e i tuoi job pianificati diventeranno noiosamente affidabili — che è esattamente ciò che vuoi che siano.