Linux v příkazech - plánované spouštění procesů

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 75245×

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.

Jednorázové spouštění procesů

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ápisVýznam
HH:MMkonkrétní čas
4AM4:00
4PM16:00
noon12:00
teatime16:00
midnight0:00
MMM DDnázev_měsíce den
MM/DD/YY nebo DD.MM.YYměsíc, den, rok
nownyní
now + n jednotekpřipočtení n časových jednotek k aktuálnímu času
todayoznačuje dnešek
tomorrowoznač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.

Vixie cron

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.

Zdroje úloh

Ú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.

Formát tabulky crontab

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 sloupceVýznamMožné hodnoty
1minuty0-59
2hodiny0-23
3dny v měsíci1-31
4měsíce1-12 nebo Jan-Dec
5dny v týdnu0-7 nebo Mon-Sun (0 i 7 platí pro neděli)
6příkaz, který se má v tuto dobu spustitpří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

Časové konstanty

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é slovoVýznam
@rebootpo startu
@yearly nebo @annually0 0 1 1 *
@monthly0 0 1 * *
@weekly0 0 * * 0
@daily0 0 * * *
@hourly0 * * * *

Proměnné prostředí

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.

Příklady

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.

fcron

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).

fcrontab

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ů.

Nastavení proměnných

Nastavování proměnných včetně speciálních USER, HOME, MAILTO a SHELL funguje stejně jako ve Vixie cronu.

Spouštění podle skutečného času

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.

Spouštění úloh každých n časových jednotek

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

Periodické spouštění v intervalech

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 typSyntaxe parametru čas
hourly, midhourlyminuty
daily, middaily, nightly, weekly, midweeklyminuty hodiny
monthly, midmonthlyminuty hodiny dny_v_měsíci
mins, hours, days, mons, dowminuty 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

Interaktivní režim

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říkazVýznam
hnápověda
qkonec
lsvýpis úloh, máte-li práva, lze zadat i uživatele
run IDspustí úlohu ID
detail IDinformace 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).

Příklad - posílání SMS s TV programem na mobil

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.

Další zdroje

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1178