Et cron-udtryk består af fem felter adskilt af mellemrum: minut, time, dag i måneden, måned og ugedag, efterfulgt af den kommando, der skal køres. 30 2 * * * betyder “kl. 02:30 hver dag”. Hvert felt tager et tal, en * for “alle værdier”, en liste med kommaer, et interval med bindestreg eller et trin med skråstreg. De fleste cron-problemer skyldes ikke selve syntaksen, men det, der omgiver den: serverens tidszone, reglen for, hvordan dag i måneden og ugedag kombineres, og det minimale miljø, cron kører kommandoer i.
Denne guide forklarer hvert felt og hvert specialtegn, giver dig en tabel med færdige udtryk og gennemgår de fejl, der får jobs til at køre på det forkerte tidspunkt eller slet ikke.
De fem felter
Hver linje i en brugers crontab følger samme opbygning:
┌───────────── minut (0–59)
│ ┌─────────── time (0–23)
│ │ ┌───────── dag i måneden (1–31)
│ │ │ ┌─────── måned (1–12 eller JAN–DEC)
│ │ │ │ ┌───── ugedag (0–7 eller SUN–SAT, både 0 og 7 er søndag)
│ │ │ │ │
* * * * * /path/to/command
Et job kører, når det aktuelle minut matcher alle felterne, med én undtagelse for de to dagfelter, som vi gennemgår nedenfor. Måneder og ugedage kan skrives som engelske navne på tre bogstaver, og der skelnes ikke mellem store og små bogstaver.
Du redigerer din egen crontab med crontab -e og viser den med crontab -l. Systemfilerne, /etc/crontab og filerne i /etc/cron.d/, har et sjette felt mellem tidsplanen og kommandoen: den bruger, jobbet kører som. At kopiere en linje fra det ene format til det andet uden at tilføje eller fjerne det felt er en klassisk måde at ødelægge et job på.
Specialtegn
TegnBetydningEksempelResultat*Alle værdier* * * * *Hvert minut,Liste af værdier0 8,12,18 * * *Kl. 08:00, 12:00 og 18:00-Interval0 9 * * 1-5Kl. 09:00 mandag til fredag/Trin*/15 * * * *Minut 0, 15, 30 og 45
Sådan virker trin i virkeligheden
Et trin deler et felts interval op, startende fra dets første værdi. */15 i minutfeltet betyder 0, 15, 30 og 45. Et trin kan også bruges på et interval: 0-30/10 betyder minut 0, 10, 20 og 30.
Trin starter forfra ved feltets grænse. */7 i minutfeltet kører ved 0, 7, 14 og så videre op til 56 og derefter ved 0 igen, så det sidste mellemrum i hver time kun er fire minutter. */7 er altså ikke “hvert syvende minut”. Klassisk cron kan heller ikke udtrykke “hvert 90. minut” på én linje; det kræver to linjer eller et tjek inde i scriptet.
Syntaks, der ikke er klassisk cron
Du støder måske på L (sidste), W (nærmeste hverdag), # (n’te ugedag i måneden) og ? i dokumentation. De stammer fra Quartz og visse cloud- og CI-schedulere, ikke fra cron på en Linux-server, og nogle af de schedulere tilføjer også et sekundfelt i starten. Mange cron-versioner afviser desuden intervaller af navne, så skriv 1-5 i stedet for MON-FRI.
Genveje som @daily
De fleste cron-implementeringer på Linux accepterer en navngivet genvej i stedet for de fem felter:
@reboot: én gang, når cron-dæmonen starter efter en genstart.
@yearly eller @annually: 0 0 1 1 *, midnat den 1. januar.
@monthly: 0 0 1 * *, midnat den første i måneden.
@weekly: 0 0 * * 0, midnat mellem lørdag og søndag.
@daily eller @midnight: 0 0 * * *.
@hourly: 0 * * * *.
Alle genveje udløses på hele timer eller ved midnat, så på en travl server kæmper alle jobs om de samme ressourcer kl. 00:00. Skriver du det fulde udtryk med et skævt minuttal, fx 17 0 * * *, fordeler du belastningen.
Færdige cron-udtryk
UdtrykKører* * * * *Hvert minut*/5 * * * *Hvert 5. minut*/15 * * * *Hvert 15. minut0 * * * *Hver time, på slaget15 */2 * * *Minut 15 hver anden time: 00:15, 02:15, 04:15 og så videre0 9-17 * * 1-5Hver time fra 09:00 til 17:00, mandag til fredag*/10 8-18 * * 1-5Hvert 10. minut i kontortiden på hverdage30 2 * * *Hver dag kl. 02:300 8,12,18 * * *Tre gange om dagen: 08:00, 12:00 og 18:000 6 * * 1Hver mandag kl. 06:000 22 * * 5Hver fredag kl. 22:000 0 * * 0Hver søndag ved midnat0 3 1 * *Den første dag i hver måned kl. 03:000 4 1,15 * *Den 1. og 15. i hver måned kl. 04:000 5 1 */3 *Hvert kvartal: 1. januar, april, juli og oktober kl. 05:000 0 1 1 *Én gang om året, ved midnat den 1. januar@rebootÉn gang efter hver systemstart
En typisk fejl i forhold til listen her er * */2 * * *. Den kører ikke hver anden time. Den kører hvert minut i hver anden time, tres gange i træk. Når du begrænser timen, skal du altid også angive minuttet.
Dag i måneden og ugedag: ELLER-reglen
Alle felter kombineres med OG, undtagen de to dagfelter. Når både dag i måneden og ugedag er begrænset, altså ingen af dem er *, kører jobbet, når ét af dem matcher. Så denne linje:
0 9 1 * 1 /usr/local/bin/report.sh
kører kl. 09:00 den første i hver måned og kl. 09:00 hver mandag, ikke kun på mandage, der falder den første. Af samme grund betyder 0 9 1-7 * 1 ikke “den første mandag i måneden”; den kører på dag 1 til 7 og hver mandag. Vil du have den første mandag, så planlæg dag 1 til 7 og tjek ugedagen i kommandoen. Bemærk det escapede procenttegn, som forklares længere nede:
0 9 1-7 * * [ “$(date +\%u)” = “1” ] && /usr/local/bin/report.sh
Klassisk cron har heller ingen “sidste dag i måneden”. Med GNU date planlægger du dag 28 til 31 og kører kun, når i morgen er den første:
0 23 28-31 * * [ “$(date -d tomorrow +\%d)” = “01” ] && /usr/local/bin/month-end.sh
Tidszoner og sommertid
Cron bruger tidszonen på det system, den kører på. Et job skrevet til Stockholm-tid på en server, der er sat til UTC, starter en eller to timer forkert afhængigt af årstiden. Tjek serverens tidszone med timedatectl eller date, før du vælger en tidsplan. Brug samme tidszone, når du overvåger jobbet: Uptimechecks cron-monitorer tager crontab-tidsplanen sammen med en tidszone, så de forventede kørselstidspunkter passer med serverens.
Nogle implementeringer, fx cronie, accepterer en CRON_TZ-variabel øverst i crontabben, så tidsplanerne kører i en anden tidszone. Understøttelsen varierer mellem cron-versioner, så test det i stedet for at gå ud fra, at det virker.
Sommertid giver de mærkeligste fejl. Om foråret findes én lokal time ikke; om efteråret sker én time to gange. Et job planlagt cirka mellem 01:00 og 03:00 lokal tid kan blive sprunget over, forsinket eller køre to gange, og hvordan hver cron-implementering håndterer skiftet, varierer. De sikreste løsninger:
Kør servere på UTC, som ikke har sommertid, og omregn i hovedet eller i en kommentar.
Skal serveren køre på lokal tid, så hold vigtige jobs uden for de timer, hvor uret stilles.
Skriv tidszonen i en kommentar over hvert job, så den næste ikke skal gætte.
Gør jobs sikre at køre to gange, fx ved at tjekke, om dagens eksport allerede findes.
Faldgruber med miljø og PATH
En kommando, der virker i din terminal, men fejler under cron, fejler næsten altid på grund af miljøet. Cron indlæser ikke din shell-profil, så meget af det, du tager for givet, mangler:
PATH er minimal, ofte kun /usr/bin:/bin. Kommandoer i /usr/local/bin eller værktøjer installeret med nvm, pyenv, rbenv eller Composer bliver ikke fundet. Brug absolutte stier, eller sæt PATH øverst i crontabben.
Shellen er /bin/sh, ikke bash. Syntaks, der kun findes i bash, som [[ ]] eller source, fejler. Sæt SHELL=/bin/bash, eller læg logikken i et script med en bash-shebang.
Procenttegn er specielle. I en crontab-linje bliver % til et linjeskift. date +%F skal skrives date +\%F eller flyttes ind i et script.
Arbejdsmappen er brugerens hjemmemappe, ikke dit projekt. Relative stier peger det forkerte sted hen. Brug cd til projektet først.
Miljøvariabler er væk. API-nøgler og database-URL’er, der eksporteres i .bashrc, findes ikke. Indlæs dem fra en fil i scriptet.
Output forsvinder. Cron mailer output til MAILTO, hvis mail er sat op, og det er det ikke på de fleste servere. Send output til en logfil.
Kørsler kan overlappe. Hvis et job, der kører hvert femte minut, nogle gange tager syv, kører to kopier samtidig. flock -n springer en kørsel over, så længe den forrige holder låsen.
Nogle cron-versioner ignorerer også en sidste linje, der ikke slutter med et linjeskift. En crontab, der tager højde for alt dette, ser sådan ud:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=””
# Natlig databasebackup. Servertiden er UTC.
30 2 * * * cd /var/www/fjordstudio && flock -n /tmp/backup.lock ./bin/backup.sh >> /var/log/backup.log 2>&1
Sådan tester du et cron-udtryk
Læs det tilbage med ord. Indsæt udtrykket i en cron-parser, og tjek, at beskrivelsen og de næste kørselstidspunkter svarer til det, du mente.
Kør kommandoen i crons miljø. Et nedskåret miljø genskaber de fleste “det virker i min terminal”-fejl, før du venter på tidsplanen.
Planlæg det til hvert minut én gang. Brug * * * * * midlertidigt, se det køre, og sæt derefter den rigtige tidsplan.
Tjek, at cron startede det. På Debian og Ubuntu: grep CRON /var/log/syslog eller journalctl -u cron; på Red Hat-baserede systemer: /var/log/cron eller journalctl -u crond.
Bekræft, at crontabben er installeret med crontab -l som den bruger, der skal køre den.
Sådan simulerer du crons miljø fra en shell:
env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=”$HOME” /bin/sh -c ‘/usr/local/bin/backup.sh’
Giv hvert job et heartbeat
Et korrekt udtryk fortæller kun cron, hvornår et job skal starte. Det siger intet om, hvorvidt jobbet kørte eller blev færdigt. En genstart, en overskrevet crontab eller en manglende variabel fejler alle i stilhed.
Et heartbeat lukker det hul. Jobbet kalder en unik URL, når det er færdigt uden fejl, og overvågningstjenesten giver dig en alarm, når kaldet ikke kommer til tiden:
30 2 * * * /usr/local/bin/backup.sh && curl -fsS -m 10 –retry 3 https://heartbeat.example.com/your-unique-id > /dev/null
Sæt heartbeatets forventede interval til samme tidsplan som cron-udtrykket, med margen til langsomme kørsler: 24 timer plus lidt for et natligt job, én time plus lidt for et job, der kører hver time. Uptimechecks overvågning af cron-jobs virker på den måde, med alarmer, når et ping mangler, og en besked, når jobbet melder sig igen. Cron-monitorerne tager crontab-tidsplanen i din tidszone, accepterer start- og slutping på én monitor, giver alarm, når en kørsel overskrider sin maksimale kørselstid, og kan gemme exitkoden og jobbets output. Guiden om at overvåge cron-jobs med heartbeats gennemgår opsætningen i detaljer, og at undgå oversprungne cron-opgaver handler om vanerne omkring det.
Når et heartbeat udebliver, ligger årsagen ofte på serveren: en fuld disk, opbrugt hukommelse eller en uplanlagt genstart. Serverovervågning ved siden af dine heartbeats viser årsagen i samme øjeblik.
Tjekliste over typiske fejl
At begrænse timen, men lade minuttet stå som *.
At sætte begge dagfelter og forvente OG i stedet for ELLER.
At gå ud fra, at serveren er i din tidszone.
At planlægge vigtige jobs i sommertidstimen.
At bruge relative stier, bash-syntaks eller % uden escape.
At køre et job uden heartbeat, så ingen opdager det, når det stopper.
Opsummering
Fem felter, fire specialtegn og en håndfuld genveje dækker næsten enhver tidsplan, du får brug for. Udtrykket er sjældent problemet. Det er tidszoner, ELLER-reglen for dagfelterne og crons nøgne miljø. Skriv tidszonen ved hvert job, brug absolutte stier, log outputtet, og giv hvert job, der betyder noget, et heartbeat.
Uptimechecks heartbeats er med i alle planer, også Free, sammen med oppetids-, SSL-, domæne- og DNS-overvågning. Start en gratis prøveperiode på 14 dage, uden kreditkort.