Řešení 1:
Jak opravit všechny vaše strasti/problémy související s crontabem (Linux)
Toto je komunitní wiki, pokud si všimnete něčeho nesprávného v této odpovědi nebo máte další informace, upravte to.
Nejprve základní terminologie:
- cron(8) je démon, který provádí naplánované příkazy.
- crontab(1) je program používaný k úpravě uživatelských souborů crontab(5).
- crontab(5) je soubor pro uživatele, který obsahuje instrukce pro cron(8).
Dále, vzdělávání o cronu:
Každý uživatel v systému může mít svůj vlastní soubor crontab. Umístění kořenových a uživatelských souborů crontab závisí na systému, ale obecně je nižší než /var/spool/cron
.
V celém systému existuje /etc/crontab
soubor /etc/cron.d
adresář může obsahovat fragmenty crontab, které cron také čte a zpracovává. Některé distribuce Linuxu (např. Red Hat) mají také /etc/cron.{hourly,daily,weekly,monthly}
což jsou adresáře, skripty, které se budou spouštět každou hodinu/den/týden/měsíc s oprávněním root.
root může vždy použít příkaz crontab; běžným uživatelům může, ale nemusí být udělen přístup. Když upravujete soubor crontab příkazem crontab -e
a uložte jej, crond zkontroluje jeho základní platnost, ale nezaručuje, že váš soubor crontab je správně vytvořen. Existuje soubor s názvem cron.deny
který určí, kteří uživatelé nemohou používat cron. cron.deny
umístění souboru je závislé na systému a lze jej odstranit, což umožní všem uživatelům používat cron.
Pokud počítač není zapnutý nebo démon crond neběží a datum/čas spuštění příkazu uplynul, crond nezachytí a nespustí minulé dotazy.
podrobnosti crontab, jak formulovat příkaz:
Příkaz crontab je reprezentován jedním řádkem. Nemůžete použít \
pro rozšíření příkazu na více řádků. Hodnota hash (#
) znak představuje komentář, což znamená, že cokoli na tomto řádku je ignorováno cronem. Úvodní mezery a prázdné řádky jsou ignorovány.
Buďte VELMI opatrní při používání procent (%
) přihlaste se ke svému příkazu. Pokud nejsou escapovány \%
jsou převedeny na nové řádky a vše za prvním neuvedeným %
je předán vašemu příkazu na stdin.
Existují dva formáty souborů crontab:
-
Uživatelské crontabs
# Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) # | | | | | # * * * * * command to be executed
-
Celý systém
/etc/crontab
a/etc/cron.d
fragmenty# Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) # | | | | | # * * * * * user-name command to be executed
Všimněte si, že druhý vyžaduje uživatelské jméno. Příkaz bude spuštěn jako jmenovaný uživatel.
Prvních 5 polí řádku představuje čas(y), kdy má být příkaz spuštěn. Ve specifikaci času můžete použít čísla nebo případně názvy dnů/měsíců.
- Pole jsou oddělena mezerami nebo tabulátory.
- Čárka (
,
) se používá k určení seznamu, např. 1,4,6,8, což znamená spustit na 1,4,6,8. - Rozsahy jsou specifikovány pomlčkou (
-
) a lze je kombinovat se seznamy, např. 1-3,9-12, což znamená mezi 1 a 3 a poté mezi 9 a 12. /
znak lze použít k zavedení kroku, např. 2/5, což znamená začít na 2 a poté každých 5 (2,7,12,17,22...). Neobtékají konec.- Hvězdička (
*
) v poli znamená celý rozsah pro toto pole (např.0-59
pro minutové pole). - Rozsahy a kroky lze kombinovat, např.
*/2
znamená začínající na minimu pro příslušné pole, pak každé 2, např. 0 pro minuty (0,2...58), 1 pro měsíce (1,3...11) atd.
Ladění příkazů cron
Zkontrolujte poštu!
Ve výchozím nastavení cron odešle jakýkoli výstup z příkazu uživateli, pod kterým příkaz spouští. Pokud nebude žádný výstup, nebude žádná pošta. Pokud chcete, aby cron posílal poštu na jiný účet, můžete nastavit proměnnou prostředí MAILTO v souboru crontab, např.
[email protected]
1 2 * * * /path/to/your/command
Zachyťte výstup sami
Můžete přesměrovat stdout a stderr do souboru. Přesná syntaxe pro zachycení výstupu se může lišit v závislosti na tom, jaký shell cron používá. Zde jsou dva příklady, které ukládají veškerý výstup do souboru /tmp/mycommand.log
:
1 2 * * * /path/to/your/command &>/tmp/mycommand.log
1 2 * * * /path/to/your/command >/tmp/mycommand.log 2>&1
Podívejte se na protokoly
Cron zaznamenává své akce prostřednictvím syslogu, který (v závislosti na vašem nastavení) často přechází na /var/log/cron
nebo /var/log/syslog
.
V případě potřeby můžete filtrovat příkazy cron pomocí např.
grep CRON /var/log/syslog
Nyní, když jsme prošli základy cronu, kde jsou soubory a jak je používat, pojďme se podívat na některé běžné problémy.
Zkontrolujte, zda je cron spuštěný
Pokud cron neběží, vaše příkazy nebudou naplánovány ...
ps -ef | grep cron | grep -v grep
měl by vám dostat něco jako
root 1224 1 0 Nov16 ? 00:00:03 cron
nebo
root 2018 1 0 Nov14 ? 00:00:06 crond
Pokud ne, restartujte jej
/sbin/service cron start
nebo
/sbin/service crond start
Mohou existovat i jiné metody; použijte to, co vaše distribuce poskytuje.
cron spustí váš příkaz v omezeném prostředí.
To, jaké proměnné prostředí jsou k dispozici, bude pravděpodobně velmi omezené. Obvykle získáte definovaných pouze několik proměnných, například $LOGNAME
, $HOME
a $PATH
.
Za zmínku stojí zejména PATH
je omezeno na /bin:/usr/bin
. Naprostá většina problémů „můj cron skript nefunguje“ je způsobena touto restriktivní cestou . Pokud je váš příkaz na jiném místě, můžete to vyřešit několika způsoby:
-
Zadejte úplnou cestu ke svému příkazu.
1 2 * * * /path/to/your/command
-
Zadejte vhodnou PATH v souboru crontab
PATH=/bin:/usr/bin:/path/to/something/else 1 2 * * * command
Pokud váš příkaz vyžaduje jiné proměnné prostředí, můžete je také definovat v souboru crontab.
cron spustí váš příkaz pomocí cwd ==$HOME
Bez ohledu na to, kde v souborovém systému je spouštěný program umístěn, aktuální pracovní adresář programu při spuštění cronu bude domovským adresářem uživatele . Pokud přistupujete k souborům ve svém programu, musíte to vzít v úvahu, pokud používáte relativní cesty nebo (nejlépe) všude používáte plně kvalifikované cesty a ušetříte všem spoustu zmatků.
Poslední příkaz v mém crontab se nespustí
Cron obecně vyžaduje, aby byly příkazy ukončeny novým řádkem. Upravte svůj crontab; přejděte na konec řádku, který obsahuje poslední příkaz a vložte nový řádek (stiskněte enter).
Zkontrolujte formát crontab
Nemůžete použít uživatelský crontab ve formátu crontab pro /etc/crontab nebo fragmenty v /etc/cron.d a naopak. Uživatelsky formátovaný crontab neobsahuje uživatelské jméno na 6. pozici řádku, zatímco systémový crontab obsahuje uživatelské jméno a spouští příkaz jako tento uživatel.
Vložil jsem soubor do /etc/cron.{hourly,daily,weekly,monthly} a neběží
- Zkontrolujte, zda název souboru nemá příponu viz run-parts
- Zkontrolujte, zda má soubor oprávnění ke spuštění.
- Řekněte systému, co má použít při spouštění skriptu (např. vložte
#!/bin/sh
nahoře)
Chyby související s datem Cronu
Pokud je vaše datum nedávno změněno aktualizací uživatele nebo systému, časovým pásmem nebo jiným, crontab se začne chovat nevyzpytatelně a vykazovat bizarní chyby, někdy fungující, někdy ne. Toto je pokus crontabu pokusit se „dělat, co chcete“, když se pod ním mění čas. Po změně hodiny pole „minuta“ přestane fungovat. V tomto scénáři by byly akceptovány pouze hvězdičky. Restartujte cron a zkuste to znovu bez připojení k internetu (takže datum nemá šanci resetovat se na jeden z časových serverů).
Znaky procenta znovu
Abychom zdůraznili rady ohledně procentních znaků, zde je příklad toho, co s nimi cron dělá:
# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz
vytvoří soubor ~/cron.out obsahující 3 řádky
foo
bar
baz
To je obzvláště rušivé při použití date
příkaz. Ujistěte se, že jste opustili znaky procent
* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
Pozor na sudo
při spuštění jako uživatel bez oprávnění root
crontab -e
otevře crontab uživatele, zatímco
sudo crontab -e
otevře crontab uživatele root. Nedoporučuje se spouštět příkazy sudo v úloze cron, takže pokud se pokoušíte spustit příkaz sudo v cronu uživatele, zkuste tento příkaz přesunout do cronu root a odebrat sudo z příkazu.
Řešení 2:
Debian Linux a jeho deriváty (Ubuntu, Mint atd.) mají některé zvláštnosti, které mohou bránit provádění úloh cronu; konkrétně soubory v /etc/cron.d
, /etc/cron.{hourly,daily,weekly,monthly}
musí :
- být ve vlastnictví uživatele root
- může zapisovat pouze uživatelem root
- nesmí zapisovat skupinami ani jinými uživateli
- mají jméno bez teček „.“ nebo jakýkoli jiný speciální znak kromě '-' a '_' .
Ten poslední ubližuje pravidelně nic netušícím uživatelům; konkrétně jakýkoli skript v jedné z těchto složek s názvem whatever.sh
, mycron.py
, testfile.pl
, atd. nebude být popraven, nikdy.
Podle mých zkušeností byl tento konkrétní bod zdaleka nejčastějším důvodem nespuštění cronjob na Debianu a jeho derivátech.
Viz man cron
v případě potřeby získáte další podrobnosti.
Řešení 3:
Pokud vaše cronjoby přestanou fungovat, zkontrolujte, zda nevypršela platnost vašeho hesla, protože jakmile tomu tak je, všechny úlohy cron se zastaví.
Zprávy budou ve formátu /var/log/messages
podobný tomu níže, který ukazuje problémy s ověřováním uživatele:
(username) FAILED to authorize user with PAM (Authentication token is no longer valid; new one required)
Řešení 4:
Neobvyklé a nepravidelné rozvrhy
Cron je všechno považováno za velmi základní plánovač a jeho syntaxe neumožňuje správci snadno formulovat trochu méně obvyklé plány.
Zvažte následující úlohu, která by se běžně vysvětlovala jako "spustit command
každých 5 minut" :
*/5 * * * * /path/to/your/command
versus:
*/7 * * * * /path/to/your/command
což ne vždy spustit command
každých 7 minut .
Pamatujte, že / znak lze použít k zavedení kroku, ale tyto kroky nepřesahují konec řady, např. */7
která odpovídá každé 7. minutě od minut 0-59
tj. 0,7,14,21,28,35,42,49,56, ale mezi jednou hodinou a další mezi dávkami budou pouhé 4 minuty , za 00:56
nová série začíná na 01:00
, 01:07
atd. (a dávky nebudou spuštěny na 01:03
, 01:10
, 01:17
atd.).
Co dělat místo toho?
Vytvořte více dávek
Namísto jedné úlohy cronu vytvořte více dávek, jejichž kombinace povede k požadovanému plánu.
Chcete-li například spustit dávku každých 40 minut (00:00, 00:40, 01:20, 02:00 atd.), vytvořte dvě dávky, jednu, která běží dvakrát v sudé hodiny, a druhou, která běží pouze v liché hodiny:
# The following lines create a batch that runs every 40 minutes i.e.
# runs on 0:00, 0:40, 02:00, 02:40 04:00 etc to 22:40
0,40 */2 * * * /path/to/your/command
# runs on 01:20, 03:20, etc to 23:20
20 1/2 * * * /path/to/your/command
# Combined: 0:00, 0:40, 01:20, 02:00, 02:40, 03:20, 04:00 etc.
Spouštějte dávky méně často
Raději než spouštět dávku každých 7 minut, což je obtížné rozvrhnout do několika dávek, jednoduše ji spouštějte každých 10 minut.
Spouštějte dávky častěji (ale zabraňte souběžnému spuštění více dávek)
Mnoho lichých plánů se vyvíjí, protože doba běhu dávek se zvětší/kolísá a poté se dávky naplánují s trochou dodatečné bezpečnostní rezervy, aby se předešlo tomu, že se následující běhy stejné dávky překrývají a běží souběžně.
Místo toho přemýšlejte jinak a vytvořte cronjob, který selže, když předchozí běh ještě neskončil, ale který poběží jinak. Viz tyto otázky a odpovědi:
* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job
To téměř okamžitě spustí nové spuštění, jakmile bude dokončeno předchozí spuštění /usr/local/bin/frequent_cron_job.
Spouštějte dávky častěji (ale pokud podmínky nejsou správné, odejděte s půvabem)
Protože syntaxe cron je omezená, můžete se rozhodnout umístit složitější podmínky a logiku do samotné dávkové úlohy (nebo ve skriptu obálky kolem existující dávkové úlohy). To vám umožní využívat pokročilé schopnosti vašich oblíbených skriptovacích jazyků, komentovat váš kód a zabrání těžko čitelným konstrukcím v samotném záznamu crontab.
V bash seven-minute-job
by pak vypadalo něco jako:
#!/bin/bash
# seven-minute-job
# This batch will only run when 420 seconds (7 min) have passed
# since the file /tmp/lastrun was either created or updated
if [ ! -f /tmp/lastrun ] ; then
touch /tmp/lastrun
fi
if [ $(( $(date +%s) - $(date -r /tmp/lastrun +%s) )) -lt 420 ] ; then
# The minimum interval of 7 minutes between successive batches hasn't passed yet.
exit 0
fi
#### Start running your actual batch job below
/path/to/your/command
#### actual batch job is done, now update the time stamp
date > /tmp/lastrun
#EOF
Které pak můžete bezpečně (pokusit) spustit každou minutu:
* * * * * /path/to/your/seven-minute-job
Jiným, ale podobným problémem by bylo naplánovat spuštění dávky na první pondělí každého měsíce (nebo druhou středu) atd. Jednoduše naplánujte spuštění dávky každé pondělí a ukončení, když datum není mezi 1 nebo 7 a dnem týdne není pondělí.
#!/bin/bash
# first-monday-of-the-month-housekeeping-job
# exit if today is not a Monday (and prevent locale issues by using the day number)
if [ $(date +%u) != 1 ] ; then
exit 0
fi
# exit if today is not the first Monday
if [ $(date +%d) -gt 7 ] ; then
exit 0
fi
#### Start running your actual batch job below
/path/to/your/command
#EOF
Které pak můžete klidně (pokusit) spustit každé pondělí:
0 0 * * 1 /path/to/your/first-monday-of-the-month-housekeeping-job
Nepoužívejte cron
Pokud jsou vaše potřeby složité, můžete zvážit použití pokročilejšího produktu, který je navržen pro spouštění složitých plánů (distribuovaných na více serverech) a který podporuje spouštěče, závislosti úloh, zpracování chyb, opakování a monitorování opakování atd. Oborový žargon by byl „podnikové " plánování úloh a/nebo "automatizace pracovní zátěže".
Řešení 5:
Specifické pro PHP
Pokud máte nějakou práci cron jako:
php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log
A v případě chyb očekávejte, že vám budou zaslány, ale ne -- zaškrtněte toto.
PHP ve výchozím nastavení neposílá chyby do STDOUT. @viz https://bugs.php.net/bug.php?id=22839
Chcete-li to opravit, přidejte do souboru php.ini cli nebo do svého řádku (nebo do svého obalu bash pro PHP) toto:
- --define display_startup_errors=1
- --define display_errors='stderr'
1. nastavení vám umožní mít fatální události jako 'Memory Oops' a 2. - přesměrovat je všechny na STDERR. Teprve poté, co budete moci dobře spát, bude vše odesláno do e-mailu vašeho roota, nikoli pouze zalogováno.