Obvykle se po zadání v shellu příkaz provede okamžitě. Jak docílit toho, aby se vykonal později?
20.4.2006 06:00 | Jiří Václavík | přečteno 75274×
Zejména pro systémové administrátory jsou nástroje pro plánované spouštění procesů nenahraditelné. Dělají za člověka spoustu práce. Čas od času využijí jejich služeb i obyčejní uživatelé.
Příkladem využití těchto nástrojů obyčejnými uživateli může být stahování velkých souborů z internetu. Je zbytečné zatěžovat síť během dne, když můžeme nechat počítač spuštěný přes noc a stahovat v době mezi 2:00 a 4:00. Pomocí nástrojů pro plánování lze zajistit, aby se stahování spustilo automaticky přesně ve 2:00. Nemusíme tak do té doby čekat, abychom stahování zahájili ručně, a dokonce ani nemusíme psát žádné složité skripty. O vše se v tomto případě postará nástroj at.
Další možností je rozsáhlejší webová aplikace. Ta se bez plánování spouštění procesů jen těžko obejde. To samé platí o pravidelném zálohování databází, vyhodnocování statistik, promazávání tmp souborů, stahování aktuálního kurzového lístku, stahování pošty nebo třeba provozování nějakého lepšího kalendáře.
Nejznámější z této kategorie nástrojů je bezesporu cron, který spouští procesy opakovaně v určitých intervalech. Dalším zástupcem je at, jež se od cronu liší tím, že procesy nespouští periodicky, ale pouze jednorázově. V článku však bude řeč ještě o dalším zástupci. Tím je fcron, který rozšiřuje vlastnosti cronu. Existují ještě další varianty cronu, ale těmi se zabývat nebudeme, protože fcron je z nich nejkomplexnější.
Aplikace, jejichž spouštění je plánované, nesmí mít interaktivní rozhraní. Pokud by totiž program potřeboval komunikovat s uživatelem, ztrácelo by jeho plánované spuštění smysl (u cronu při definici úloh standardní vstup částečně zadávat lze, nicméně to nemůžeme považovat za interaktivitu). Vhodnými kandidáty pro toto použití jsou z těch nejběžnějších například wget, lynx -source, cp, mv, rm, apod. Samozřejmě si můžeme napsat skript vlastní.
Nyní pojďme prozkoumat již ony konkrétní nástroje.
Nástroj at se používá k ukládání příkazů, které mají být provedeny později v předem definovaný čas.
Provádění naplánovaných úloh má na starosti démon atd. Úlohy pro at lze sice definovat i bez spuštěného démona, ale pokud není spuštěn v době, na kdy je úloha definovaná, nedojde k jejímu provedení. Respektive dojde, ale až v okamžiku, kdy je atd spuštěn. Proto je nutné mít zapnutého démona stále.
# /etc/init.d/atd start
Parametrem příkazu at je plánovaný čas vykonání. Pomocí přepínače -f lze zadat i soubor, který bude v určený okamžik proveden. Pokud soubor neuvedeme, objeví se prompt, kam příkazy zadáme. Zadávání ukončíme stiskem C-d.
$ at 3:00
warning: commands will be executed using /bin/sh
at> wget www.server.cz/film.mpg
at> <stisk C-d><EOT>
job 51 at 2006-04-11 22:30
$
Třetí možnost zadání příkazů je neinteraktivní a tudíž nejlépe použitelná uvnitř skriptů.
$ echo 'wget www.server.cz/film.mpg' | at 3:00
Formát času je poměrně volný, protože at podporuje velké množství předdefinovaných konstant. Možné jsou například zápisy uvedené v následující tabulce. Podrobnější výklad formátu je pak v at(1p).
Zápis | Význam |
HH:MM | konkrétní čas |
4AM | 4:00 |
4PM | 16:00 |
noon | 12:00 |
teatime | 16:00 |
midnight | 0:00 |
MMM DD | název_měsíce den |
MM/DD/YY nebo DD.MM.YY | měsíc, den, rok |
now | nyní |
now + n jednotek | připočtení n časových jednotek k aktuálnímu času |
today | označuje dnešek |
tomorrow | označuje zítřek |
Časovými jednotkami se rozumí jeden z následujících řetězců: minutes, hours, days, weeks, months, years nebo případně jejich jednotná čísla.
Pokud chceme naplánovat úkol na 21. prosince a 15:00, použijeme například zápis 3pm Dec 21 nebo 15:00 12/21/06. Pro okamžik za 10 minut zadáme now + 10 minutes. Dalšími příklady mohou být 3am Monday, now next day, now + 1 week, 1am tomorrow a další.
V souvislosti s frontou příkazů, které mají být v určený čas provedeny, existují další příkazy atq a atrm. Již z názvu je patrné, že atq (at queue) zobrazuje frontu. Uživateli se zobrazí pouze jeho fronta, ale administrátor uvidí naplánované příkazy všech uživatelů. atrm (at remove) maže z fronty položky, jejichž ID jsou zadána mezi parametry. ID je ve výpisu atq v prvním sloupci.
Právo na používání nástroje at lze regulovat. Soubor /etc/at.allow obsahuje jména uživatelů, kteří smějí používat at. Neexistuje-li, hledá se soubor /etc/at.deny, kde jsou naopak uživatelé se zakázaným přístupem. Pokud neexistuje ani jeden z nich, at smí používat pouze administrátor.
Existuje speciální varianta příkazu at, která provádí akci pouze při systémovém zatížení menším než 0.8. Jmenuje se batch a jeho použití je podobné jako použití at.
Cron není jediný. Existuje několik jeho variant. Většina linuxových distribucí obsahuje takzvaný Vixie cron, pojmenovaný po svém autorovi. Mezi další druhy patří například Anacron nebo fcron. Druhým jmenovaným se dnes budeme také zabývat, protože nabízí o trochu větší možnosti než Vixie cron. Prozatím ho však nechme stranou a pusťme se do Vixie cronu.
Vixie cron využívá ke své činnosti cron, což je démon, běžící na pozadí, který zajišťuje periodické spouštění programů podle předpisů v danou dobu. To, zda běží, zjistíme příkazem
/etc/init.d/crond status
Cron spouští procesy v intervalech, které mají minimální délku 1 minuty. Většinou není třeba jemnější rozlišení. Pokud však skript potřebujeme spouštět jednou za 20 sekund, použijeme buď obyčejný cyklus nebo stojí za to vyzkoušet fcron.
Úlohu pro cron lze nastavit dvěma způsoby. Buď vložením skriptů do jednoho ze souborů /etc/cron.daily, /etc/cron.hourly, /etc/cron.weekly a /etc/cron.monthly a nebo použití crontab.
Cron prohledává adresář /var/spool/cron/tabs, kde se nacházejí tabulky naplánovaných úloh jednotlivých uživatelů, dále soubor /etc/crontab a dále také soubory v adresáři /etc/cron.d.
Pro editaci úloh se používá nástroj crontab. Každý uživatel má svoji crontab tabulku, která obsahuje seznam úloh a časy jejich spouštění. Po zavolání crontab s přepínačem -e se spustí editor, kde můžeme seznam úloh upravovat.
$ crontab -e
Poznámka - Editor, který se spustí, je určen proměnnou prostředí $EDITOR (někdy též $VISUAL). Budeme-li ho chtít nastavit například na vim, je třeba zapsat do konfiguračního souboru (záleží na nastavení, například $HOME/.bashrc) příkaz
$ export EDITOR=/usr/bin/vim
Každá úloha v crontabu zabírá právě jeden řádek, přičemž řádky prázdné nebo začínající na # jsou ignorovány. Řádek je rozdělen do šesti sloupců, oddělených bílými znaky. Sloupce reprezentují následující údaje.
Číslo sloupce | Význam | Možné hodnoty |
1 | minuty | 0-59 |
2 | hodiny | 0-23 |
3 | dny v měsíci | 1-31 |
4 | měsíce | 1-12 nebo Jan-Dec |
5 | dny v týdnu | 0-7 nebo Mon-Sun (0 i 7 platí pro neděli) |
6 | příkaz, který se má v tuto dobu spustit | příkaz |
Sloupce 3 a 5 jsou v logickém vztahu NEBO. To znamená, že úloha je provedena, pokud aktuálnímu datu vyhovuje alespoň jeden z těchto sloupců (a všechny sloupce zbývající). Pokud do jednoho z nich zadáme hvězdičku, nebere se na něj ohled. Poznámka - fcron umí zaměňovat vztahy NEBO a A pomocí voleb dayand resp. dayor.
Ve sloupcích určujících čas spouštění lze užít mimo jednoduchých hodnot i některé speciální znaky.
Pokud do některého sloupce zapíšeme místo konkrétního čísla hvězdičku, bude to stejně, jako by byly zadané všechny možné hodnoty v tomto sloupci. Více konkrétních hodnot v jednom sloupci se odděluje čárkami. Dále jsou povolené rozsahy. Zápis 12-15,22 v sloupci dny v měsíci znamená 12., 13., 14., 15. a 22. den v měsíci.
Implicitně je u rozsahů krokem 1 jednotka. Lze to však změnit, takže pro spuštění úlohy jednou za 4 hodiny můžeme místo zápisu 0,4,8,12,16,20 použít kratší variantu */4, kde hvězdička je rozsah (jako 0-23/4) a číslo 4 za lomítkem určuje právě krok.
Dalším znakem se speciálním významem, který se může vyskytovat v příkazu, je %. Znak procenta má význam znaku nového řádku. Vše, co bude za tímto znakem, bude spuštěný program číst ze standartního vstupu.
Pokud má naše úprava crontabu nepovolenou syntaxi, budeme upozorněni.
Chceme-li pouze úlohy zobrazit, použijme příkaz
crontab -l
Místo úvodních pěti sloupců lze použít pro speciální případy také jediný, který obsahuje jedno z následujících klíčových slov.
Klíčové slovo | Význam |
@reboot | po startu |
@yearly nebo @annually | 0 0 1 1 * |
@monthly | 0 0 1 * * |
@weekly | 0 0 * * 0 |
@daily | 0 0 * * * |
@hourly | 0 * * * * |
Velmi často jsou zdrojem záhadných chyb při provádění úloh špatně nastavené proměnné prostředí. Proto existuje způsob, jak je pro příkazy v crontabu nastavit. V crontabu lze pro tento účel uvádět řádky s následující syntaxí:
proměnná=hodnota
Zajímavá pro nás může být proměnná MAILTO. Obsahuje adresu uživatele, kam bude poslán email s výstupem příkazu. Takže po zadání
MAILTO=user@server.cz
bude výstup z naplánovaných úloh poslán emailem na user@server.cz. Pokud o výstup příkazu nemáme zájem, lze ho přesměrovat do /dev/null
Dalšími speciálními proměnnými jsou SHELL obsahující jméno příkazového interpretu, jež bude provádět naplánované úlohy a PATH, která určuje umístění, kde se hledají skripty.
Pro spuštění skriptu /home/www/cgi-bin/odesli_statistiky.cgi každý první den v měsíci v čase 1:00 přidejme do crontab následující řádek.
0 1 1 * * /home/www/cgi-bin/odesli_statistiky.cgi
Nutno také připomenout, že existuje adresář /etc/cron.monthly nebo klíčové slovo @monthly.
@monthly /home/www/cgi-bin/odesli_statistiky.cgi
Předpis další úlohy zařídí spouštění skriptu /home/aktualizuj_data po každém startu systému.
@reboot /home/aktualizuj_data
Podobně jako v prvním příkladu můžeme napsat úlohu, která jednou za 12 hodin zazálohuje databázi.
0 0,12 * * * /home/www/db_backup
Zapomínáte na narozeniny svých blízkých? Potom si nechte s dostatečným předstihem poslat email.
0 10 10 28 * echo Narozeniny! | mail -s Narozeniny ja@neco.cz
Tento příklad každý všední den v 0:00 stáhne statistiku.
0 0 * * 1-5 wget http://www.neco.cz/statistika.php
Nyní něco složitějšího. Každé úterý, středu, čtvrtek a navíc každý 5., 10., 15., 20., 25. a 30. den v měsíci vždy v 15:30 se spustí vzdálený skript www.nekde.cz/cgi-bin/run.cgi. Zde jsou varianta bez rozsahů a varianta s nimi.
30 15 5,10,15,20,25,30 * tue,wed,thu lynx -source www.nekde.cz/cgi-bin/run.cgi
30 15 5-31/5 * tue-thu lynx -source www.nekde.cz/cgi-bin/run.cgi
A nakonec úloha která, každou čtvrthodinu synchronizuje čas operačního systému podle NTP serveru ntps1-2.uni-erlangen.de. Opět ve dvou variantách.
0,15,30,45 * * * * /usr/bin/rdate -s ntps1-2.uni-erlangen.de &> /dev/null
*/15 * * * * /usr/bin/rdate -s ntps1-2.uni-erlangen.de &> /dev/null
Uživatele, kteří mohou používat cron, lze filtrovat. K tomu slouží, podobně jako u příkazu at, soubory cron.allow a cron.deny. Pokud existuje soubor cron.allow, pak uživatelé, kteří v něm nejsou uvedeni, nemohou cron používat. Pro cron.deny to platí obráceně. cron.allow má vyšší prioritu. Lze použít speciální jméno ALL, které zastupuje všechny uživatele. Pokud ani jeden ze souborů neexistuje, cron je přístupný jen administrátorovi.
Narozdíl od Vixie cronu, fcron pravděpodobně v systému nemáte. Na stránce projektu si tedy stáhněte nejnovější verzi a nainstalujte klasicky přes ./configure && make; make install.
O spouštění úloh se stará démon fcron.
Opět zde existují soubory /etc/fcron.allow a /etc/fcron.deny. K dispozici je také konfigurační soubor /etc/fcron.conf, jehož syntaxi najdeme v manuálové stránce fcron.conf(5).
Stejně jako u Vixie cronu má každý uživatel svoji tabulku úloh. Právě v tabulce úloh narazíme na největší rozdíly mezi oběma crony.
Uživatelské tabulky úloh fcronu se ukládají do /var/spool/fcron zároveň v textové a binární podobě.
V tabulce fcrontab jsou ignorovány prázdné řádky a komentáře začínající znakem #. Ostatní řádky mohou mít jeden z následujících významů.
Nastavování proměnných včetně speciálních USER, HOME, MAILTO a SHELL funguje stejně jako ve Vixie cronu.
Podobně jako Vixie cron umí fcron spouštět program podle skutečného času. Formát záznamu v fcrontabu vypadá následovně:
&volby minuta hodina den_v_měsíci měsíc den_v_týdnu příkaz
Volby jsou nastavení konkrétní pro tento příkaz. Mohou být prázdné nebo obsahovat různé parametry, oddělené čárkami. Například parametr mailto určuje, kam se bude odesílat výstup a bootrun říká, že pokud je počítač vypnutý, provede se úloha po jeho startu.
&mailto(user),bootrun 25 15 * * * /home/user/nejaka_cinnost.sh
Alespoň pro hrubou představu bychom měli přelétnout možnosti voleb v oddílu OPTIONS v fcrontab(5), protože některé jsou zajímavými rozšířeními oproti klasickému cronu a je užitečné o nich alespoň tušit.
Za rozsahem lze též užít znak ~, který z uvedeného rozsahu odstraní hodnoty za ~. Například 10-20~12~14 je stejné jako 10,11,13,15,16,17,18,19,20.
V situacích, kdy potřebujeme spustit úlohu jednou za určitou časovou jednotku, zapíšeme do fcrontab následující řádek.
@volby opakování příkaz
Opakování je časový interval, ve kterém se má opakovat příkaz. Je uveden ve speciálním formátu, ve kterém se opakují hodnoty a jednotky. Jednotka je vždy počáteční znak anglického názvu. Zápis 5d2h10m tedy znamená 5 dní, 2 hodiny a 10 minut.
Jako příklad si uvedeme počítání minut, po kterých je počítač zapnutý. Budeme mít soubor count, jenž obsahuje aktuální počet minut. Dále si napíšeme skript increment.sh, který bude každé spuštění inkrementovat hodnotu v count.
x=`/bin/cat /cesta/k/count`
x=$((x+1));
echo $x > /cesta/k/count;
A nyní řádek ve fcrontabu.
@ 1m sh /cesta/k/increment.sh
V tomto případě jsme nechali volbu prázdnou. Pokud bychom chtěli případný výstup posílat emailem uživateli user, použijeme volbu mail.
@mail(user) 1 sh /cesta/k/increment.sh
Nakonec si ukážeme syntaxi, která zajistí, že skript bude spuštěn někdy v době od a do určitého času. Můžeme tak zadefinovat například to, aby se skript spustil někdy mezi 8:00 a 12:00, jakmile to bude možné. Syntaxe takové úlohy je následující.
%typ,volby čas příkaz
Existují tři druhy konstant pro parametr typ.
*ly konstanty znamenají, že se úloha spustí někdy v daném intervalu a spustí se vždy pouze jednou. Interval hourly tedy říká, že úloha se spustí mezi 0:00 a 24:00.
mid*ly konstanty spouštějí úlohy od středu daného intervalu do středu příštího intervalu.
Poslední druh spustí příkaz jednou v každém z uvedených intervalů mimo intervalů s nižší jednotkou času, než je jednotka uvedená. Pro pochopení budou dále následovat názorné příklady.
Dalším parametrem je čas. Význam a zápis se odvíjí od předchozího argumentu.
Parametr typ | Syntaxe parametru čas |
hourly, midhourly | minuty |
daily, middaily, nightly, weekly, midweekly | minuty hodiny |
monthly, midmonthly | minuty hodiny dny_v_měsíci |
mins, hours, days, mons, dow | minuty hodiny dny_v_měsíci měsíce dny_v_týdnu |
Tato fakta si nyní předvedeme na úkazkových záznamech ve fcrontab.
Následující úloha znamená, že bude proveden příkaz /usr/bin/prikaz každý den někdy mezi 2:00 a 10:59 nebo mezi 14:00 a 22:59. Pokud tedy zapneme počítač (resp. fcron démon) v 19:30, spustí se úloha v 19:30. Pokud ho zapneme ve 12:00, provede se až ve 14:00. A pokud běží počítač stále, spustí se příkaz už ve 2:00.
%daily * 2-10,14-22 /usr/bin/prikaz
Pro spuštění jednou týdně někdy mezi 19:00 a 22:59 jakmile to půjde, lze napsat toto.
%weekly * 19-22 /usr/bin/prikaz
Nyní tento příkaz spustíme každý den v 19:30, 20:30, 21:30 a 22:30. Tedy čtyřikrát.
%mins 30 19-22 * * * /usr/bin/prikaz
A teď ho spustíme v 19:30, 20:30, 21:30 nebo 22:30. Zaměníme mins za hours, čímž se nebudou rozlišovat intervaly určené minutami. Hodnota 30 ve sloupci minut teď bude sloužit jen jako čas spuštění. Nebude totiž rozdělovat interval 19-22 v sloupci hodin na 4 samostatné intervaly, jako tomu bylo v předchozí ukázce.
%hours 30 19-22 * * * /usr/bin/prikaz
fcron nabízí příkaz fcrondyn, pomocí něhož lze modifikovat tabulku fcrontab i bez přímé editace souboru. S přepínačem -i se spustí interaktivní režim, v němž lze používat mimo jiné tyto příkazy.
Příkaz | Význam |
h | nápověda |
q | konec |
ls | výpis úloh, máte-li práva, lze zadat i uživatele |
run ID | spustí úlohu ID |
detail ID | informace o úloze ID |
Tyto příkazy lze spouštět i z příkazového řádku pomocí přepínače -x. Více o fcrondyn a možných příkazech interaktivního režimu na fcrondyn(1).
Nakonec si ukážeme něco praktického, kde využijeme jak znalosti cronu, tak znalosti příkazu at. Vytvoříme program, který bude stahovat televizní program a který bude podle klíčových slov sám vybírat pořady. Na ty pak v okamžiku jejich začátku upozorní zprávou na mobil. Program nebudeme pro názornost nijak ošetřovat.
Nejprve se podívejme na zdrojový kód programu (soubor /cesta/k/programu/porady). Ten ze všeho nejdříve stáhne HTML kód, ve kterém je obsažen televizní program. Dále zde máme definována klíčová slova pomocí přijatých argumentů. Program prohledá celý televizní program a pokud nalezne zadaná klíčová slova, zařadí pořad do vybraných pořadů.
Na konci programu se pak vybrané pořady předají příkazu at, který z nich udělá naplánované úlohy.
Jako jazyk, ve kterém je skript napsán, zvolme Perl, protože budeme muset parsovat HTML dokument a získat z něj data pomocí regulárních výrazů. Perl samozřejmě není podmínkou, ale je to díky svému zaměření vhodná volba. Jinak můžeme zvolit podle svého vkusu prakticky libovolný skriptovací jazyk s podporou regulárních výrazů.
Adresu user@vodafonemail.cz změňme na svůj osobní SMS email. Kdo nemá SMS email, může zadat i skutečný email, ale potom tato aplikace ztrácí část svého významu.
#!/usr/bin/env perl
use strict;
use warnings;
use WWW::Mechanize;
use POSIX;
my $mail = 'user@vodafonemail.cz';
my @klicova_slova = @ARGV;
my %vybrane_porady;
my $datum = strftime("%Y-%m-%d",localtime(time));
my $url = "http://vtelevizi.cz/?datum=$datum";
my $mech = WWW::Mechanize->new();
my $zdroj = ($mech->get($url))->{'_content'};
while($zdroj =~ /
<a\ itemprop=\"url\".*?title=\"([^\"]*?)">
<span[^>]*?>[^<]*?<\/span>\s*<\/a><br\s*\/><span[^>]*?><time[^>]*?>
(\d\d\:\d\d)
/igx){
my $popis = $1;
my $cas = $2;
for (@klicova_slova){
$vybrane_porady{$popis} = $cas if $popis =~ /$_/;
}
}
for (keys %vybrane_porady){
system("echo \'echo \"$_\" | mail $mail -r tv\@tip.cz\' | at ".$vybrane_porady{$_});
}
Nyní zbývá editovat crontab. Přidáme následující řádek:
0 5 * * * /cesta/k/programu/porady matrix dokument
Nový televizní program stahujeme až v 5:00 (lze to ale udělat tento den kdykoliv do 6:00), protože dříve stejně v programu žádné pořady nejsou (den podle všeho v tv programu začíná a končí v 6:00).
Nyní bychom měli vždy, když bude na televizní obrazovce začínat film Matrix nebo nějaký dokumentární film, obdržet upozornění na mobilní telefon.
Pokud vám výše uvedená aplikace nefunguje, je nejpravděpodobnějším důvodem, že v době stahování tv programu nebo v době odesílání emailu máte vypnutý počítač anebo neběží démoni atd a crond.