Как заставить cron выполнять наши приказания во FreeBSD


Dru Lavigne
перевод Станислава Лапшанского
Впервые опубликован: http://www.computerra.ru.

Было бы здорово, если бы ваша FreeBSD постоянно автоматически выполняла свое техническое обслуживание и докладывала бы вам об этом по электронной почте. К счастью, благодаря демону cron и обслуживающим сценариям, ваша FreeBSD регулярно делает это. В сегодняшней статье мы обсудим, как демон cron выполняет запланированные команды и обучимся, как сконфигурировать cron для запуска ваших сценариев и программ.

Так же как большинство других демонов, cron запускается вместе с FreeBSD и потом тихонько работает в фоновом режиме. Чтобы доказать себе, что нет необходимости запускать cron самостоятельно, поищите слово "cron" в списке запущенный процессов, например таким образом:
ps -ax | grep cron

У вас должно получиться нечто похожее:
97 ?? Is 0:07.71 cron

В данном примере cron имеет 97-й номер процесса.

Cron "просыпается" каждую минуту и проверяет списки запланированных заданий - crontab (сокращение от "chron or time table"), на предмет наличия там заданий, подлежащих запуску. Crontab - это обычный файл, содержащий список команд, а также время в которое эти команды должны запуститься. Когда вы установили FreeBSD, системный список запланированных заданий был создан автоматически. Вы не должны производить никаких изменений в этом файле. Далее мы опишем, как, используя утилиту crontab, вы сможете создавать пользовательские списки запланированных заданий, которые cron будет выполнять помимо системного списка.

Системный список хранится в файле /etc/crontab. Воспользуйтесь командой more, для того чтобы просмотреть этот файл, гарантированно не отредактировав его. Возможно вам следует войти в систему сразу на двух консолях, это позволит вам на одной консоли читать системный список заданий, а на другой выполнять различные команды. Напишите на первой консоли:
more /etc/crontab


В результате вы увидите следующие строки:
# /etc/crontab - root's crontab for FreeBSD
#
# $FreeBSD: src/etc/crontab,v 1.21 1999/12/15 17:58:29 obrien Exp $
#
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin
HOME=/var/log
#
#minute hour mday month wday who command
#
*/5 * * * * root /usr/libexec/atrun

Как и в любом файле, комментарий начинается с символа решетки "#". Наиболее полезны комментарии, описывающие содержимое полей этого файла. Заметьте - пять полей описывают время, потом идет поле пользователя (поле "кто"), а затем команда. На первый взгляд, формат этого файла может показаться вам странным, но каждая строка этого файла, не являющаяся комментарием, просто говорит cron'у, когда следует запустить указанную команду.

В следующей таблице показаны границы допустимых значений аргументов столбцов, описывающих время: Поле Допустимое значение
Минуты (minute) 0-59
Час (hour) 0-23
День месяца (dayofmonth) 1-31
Месяц (month) 1-12 или первые три буквы английского названия месяца (регистр не учитывается)
День недели (dayofweek) 0-7 (где 0 и 7 это воскресенье) или первые три буквы английского названия дня в неделе (регистр не учитывается)


Значения могут быть числом, трехбуквенным названием, а также диапазоном, например запись "1-5" в поле dayofweek будет означать "с понедельника по пятницу". Значения могут отделяться запятыми: "1,15,31" в поле dayofmonth будет запускать указанную команду 1-го, 15-го и 31-го числа каждого месяца.

Все пять полей времени допускают использование символа звездочка - "*", который обозначает "использовать любое допустимое значение" для этого поля. Например для того что бы запускать команду каждый месяц, поставьте "звездочку" в поле month.

Также можно использовать значения вида "*/число". Например "*/2" в поле месяца будет означать "запускать каждый второй месяц". В системном списке заданий есть подобный пример:
#minute hour mday month wday who command
*/5 * * * * root /usr/libexec/atrun

Эта строка читается так: выполнять команду /usr/libexec/atrun от пользователя root, если текущая минута делится нацело на пять (или просто каждые пять минут), каждый час каждый день любого месяца. Если вы не в курсе, что делает команда atrun, напечатайте на консоли:
whatis atrun

В ответ будет сказано:
atrun(8) - run jobs queued for later execution


Т.е. команда atrun запускает задачи из очереди отложенного выполнения. Если ваше любопытство не удовлетворено таким объяснением, то загляните в man 8 atrun.

И заключительная таблица, перед тем как снова обратиться к /etc/crontab. Вы можете заменить все пять полей времени следующими подстановками:
Строка Что это значит
@reboot Запускать при начальной загрузке
@yearly Заменяет "0 0 1 1 *" т.е. "ежегодно в 00:00 1 января"
@annually Тоже что и yearly
@monthly Заменяет "0 0 1 * *" т.е. "ежемесячно в 00:00 1 числа"
@weekly Заменяет "0 0 * * 0" т.е. "еженедельно в 00:00 воскресенье"
@daily Заменяет "0 0 * * *" т.е. "ежедневно в 00:00"
@midnight Тоже что и daily
@hourly Заменяет "0 * * * *" т.е. "ежечасно в 00 минут"

Вернемся в первую консоль и продолжим чтение /etc/crontab:
# rotate log files every hour, if necessary
# (произвести ротацию журналов каждый час, если это необходимо)
0 * * * * root newsyslog

Т.е. производить ротацию файлов журналов каждый час, если это необходимо. Опять комментарий оказался полезен. Для того что бы узнать что делает команда newsyslog напишите:
whatis newsyslog

Система ответит:
newsyslog(8) - maintain system log files to manageable sizes

Т.е. newsyslog предназначен для придания системным журналам управляемых размеров.

Итак, читаем /etc/crontab дальше:
# do daily/weekly/monthly maintenance
# (проводить ежедневное/еженедельное/ежемесячное обслуживание)
59 1 * * * root periodic daily 2>&1 | sendmail root
30 3 * * 6 root periodic weekly 2>&1 | sendmail root
30 5 1 * * root periodic monthly 2>&1 | sendmail root


Обратите внимание, что обслуживающие сценарии никогда не запускаются одновременно. Ежедневное обслуживание запускается каждую ночь в 1:59, если вы спите рядом с вашей FreeBSD-системой, то вы можете услышать треск винчестера в это, казалось бы, странное время. Еженедельное обслуживание проводится каждую субботу, ночью в 3:30. Ежемесячное - запускается в первый день каждого месяца в 5:30 утра. Вообще, это неплохая мысль - запускать обслуживание, когда процессор не загружен работой (т.е. в середине ночи), и не обрабатывать сразу все сценарии одновременно, для предотвращения излишней загрузки процессора вашей системы.

Комбинация 2>&1 говорит интерпретатору команд, что все ошибки и сообщения должны быть сохранены вместе. При помощи канала они передаются программе sendmail, которая отсылает их пользователю root, таким образом весь вывод (вместе с ошибками), порождаемый обслуживающими сценариями, будет отослан по электронной почте пользователю root. (На самом деле в текущих версиях FreeBSD процесс протекает несколько иначе - сценарий periodic формирует почтовое сообщение сам. Следовательно необходимости в 2>&1 | sendmail root уже нет - прим. переводчика).

Где cron находит обслуживающие сценарии? На второй консоли попробуйте напечатать:
# ls /etc/periodic
daily monthly weekly

# ls -F /etc/periodic/weekly
300.uucp* 330.catman* 310.locate*
340.noid* 120.clean-kvmdb* 320.whatis*
999.local*

Ключ -F добавляет к названиям всех запускаемых файлов символ звездочки "*". Таким образом мы видим, что /etc/periodic содержит подкаталоги, в которых содержатся сценарии, которые, в свою очередь ежедневно (daily), еженедельно (weekly) и ежемесячно (monthly) выполняет cron (тут автор статьи умалчивает о том, что на самом деле сценарии из этих подкаталогов выполняет сценарий /usr/sbin/periodic, который и указан в файле /etc/crontab - прим. переводчика). Если вы действительно любознательны, попробуйте написать:
more /etc/periodic/weekly/310.locate

Если вы учитесь писать сценарии для интерпретатора команд, исследование встроенных в систему сценариев даст вам множество примеров правильного и общепринятого синтаксиса.

Поскольку результаты работы этих сценариев отправляются по почте пользователю root, он, используя почтовую программу, может контролировать возникающие в системе неполадки. Обычно вы не конфигурируете ваш любимый почтовый клиент для получения почты пользователя root, так как в большинстве ситуаций вам не требуется получать или отправлять почту от имени пользователя root (мягко говоря спорное утверждение - прим. переводчика). Однако вы все же хотите получать уведомления о проблемах в системе, которые пришлет вам cron. Зарегистрируйтесь в системе как суперпользователь и воспользуйтесь вашим любимым почтовым клиентом для получения этой почты (большинство почтовых клиентов не требуют никакого конфигурирования для чтения почты любого пользователя - необходимо только зарегистрироваться в системе от имени желаемого пользователя и запустить клиент - прим. переводчика).

Если вы никогда не делали этого до этого, то вполне возможно, что вы получите сотни сообщений, в зависимости от того, сколько времени включен ваш компьютер (в день обычно генерируется два сообщения, не считая еженедельных и ежемесячных отчетов - прим. переводчика). Однако, если вы всегда выключаете компьютер до полуночи, то вероятно вы не получите вообще ни одного сообщения, так как все сценарии запускаются после полуночи.

Если у вас есть несколько сообщений, то, помимо прочего, вы должны получить как минимум одно, названное "daily run output", если вы прочтете это сообщение, то вы увидите, сколько работы проделывает для вас cron каждую ночь в 1:59. Обратите внимание, что сообщения, относящиеся к безопасности, присылаются отдельным письмом, которое называется "security check output". Это сообщение весьма полезно для прочтения, так как оно содержит информацию о проверке файлов, имеющих атрибут setuid, uid 0, пользователях без паролей, сообщениях ядра, отвергнутых попытках зарегистрироваться в системе и отклоненных соединениях.

После прочтения эти сообщения могут быть безопасно удалены. После выхода из почтового клиента не забудьте завершить сеанс суперпользователя.

До сего момента мы исследовали системный файл crontab, который не следует изменять. Теперь мы поглядим, как создавать свой собственный файл crontab, для того чтобы заставить cron выполнять наши команды. Во FreeBSD по умолчанию любой пользователь имеет право создать собственный файл crontab. Эти файлы хранятся в каталоге /var/cron/tabs. Если вы напечатаете (от пользователя root):
ls /var/cron/tabs


то вы обнаружите, что по умолчанию там пусто. Создавая crontab, вы создаете файл в этом каталоге, с именем пользователя. Никогда не редактируйте этот файл, используя текстовый редактор, вместо этого для редактирования и создания используйте утилиту crontab.

Вы можете быть удивлены, сколько разных команд и сценариев следует поручить cron'у для исполнения. Спросите себя, какие команды вы выполняете регулярно или должны выполнять регулярно, но иногда об этом забываете. Например, вы можете захотеть удалять core-файлы или dead.letter (т.е. обычно ненужные файлы - прим. переводчика). Или, возможно вам хочется регулярно очищать кеш вашего браузера.

Когда вы определитесь, что вы хотите, чтобы cron сделал для вас, подумайте какими командами, или наборами команд, могут быть выполнены эти задачи. Каждая запись в crontab'е должна состоять из одной строки. Вполне нормально, если строка будет переноситься по границе экрана пока вы не нажмете клавишу "Enter". Однако если команда слишком длинна, то может быть проще создать для нее отдельный файл (сценарий) и указать в crontab'е уже этот файл. Покажем это на примерах.

Если я хочу найти и удалить файлы с определенными расширениями, то я могу ввести подобную команду (вы должны ввести ее на одной строке):
find / ( -name "*.core" -or -name "dead.*" )
-print -exec rm -rf {} ;

Посмотрим, как можно интерпретировать эту малопонятную фразу. В общем виде эта команда что-то ищет. Поисковые выражения всегда выглядят как:
find точка_начала_поиска искать_вот_это с_найденным_делать_это

Итак, когда я пишу find /, я говорю команде find искать от корня файловой системы.

Дальше я говорю find, что бы она искала файлы, чьи имена заканчиваются на ".core" или начинаются на "dead.". Так как в конечном итоге я хочу что бы оба типа фалов были удалены, то я окружила их скобками. Я добавила перед скобками по обратному слешу для того что бы интерпретатор команд не воспринял их на свой счет.

Когда find найдет такие файлы, он применит к ним команду "rm -rf", указанную в параметре "-exec". Всякий раз, когда вы используете предложение "-exec" в find, вы должны заканчивать его символами ";", иначе ничего не будет работать. Фигурные скобки указывают exec место, куда следует подставить информацию найденную find.

Ну что, мне кажется это было не так уж плохо? Теперь создадим простой сценарий для очистки кеша браузера Netscape. Этот сценарий я взяла из списка рассылки FreeBSD - отсюда и еще отсюда.

Откройте ваш любимый текстовый редактор и напечатайте следующий текст:
#!/bin/sh

# First, double-check that the user is not
# currently using Netscape
# Then remove the contents of all the subdirectories
# in the Netscape cache
# Во-первых двойная проверка, не использует ли
# пользователь сейчас Netscape. Если нет, то удалить
# содержимое кеша Netscape

if ! (`ps wxu $USER | grep -q [n]etscape`)
then
echo "Clearing Netscape cache..."
rm -rf ~/.netscape/cache/*

fi

echo "Exiting...."

Попробуем разобраться, что мы тут написали. Все сценарии начинаются вот с такой штуки:
#!

за которой следует полный путь к программе которая будет выполнять сценарий. Мы создали сценарий для интерпретатора команд Bourne и указали, что команда sh (Bourne shell) будет интерпретировать этот скрипт:
#!/bin/sh

Далее мы включили семь строк комментариев (они начинаются с символа решетки "#"), для того чтобы не забыть, для чего этот сценарий предназначается.

Далее идет полезное содержимое сценария. Оно содержит предложение "если", начинающееся ключевым словом "if" и заканчивается "fi". В первой строке предложения "если" указывается условие которое может принимать значение "истина" или "ложь":
if ! (`ps wxu $USER | grep -q [n]etscape`)

Знак "!" - это операция отрицания "не", которая позволяет блоку "if" выполняться, когда выражение в скобках не истинно. По существу, команда ps используется для поиска запущенного процесса Netscape у данного пользователя. В случае отсутствия такого процесса сценарий продолжается выводом строки заключенной в кавычки:
echo "Clearing Netscape cache..."

Далее идет непосредственно очистка кеша Netscape, который находится в домашнем каталоге пользователя:
rm -rf ~/.netscape/cache/*


В случае, если у пользователя существует запущенный процесс Netscape, интерпретатор выходит из блока "if", не выполняя пункт "then".

Сохраните ваш сценарий. Я сохранила его под именем "clean". Вы же можете назвать ваш сценарий так, как вам будет угодно, однако не следует называть его именем какой-нибудь существующей команды. Для перестраховки запустите на второй консоли следующую команду:
whereis -b предполагаемое_имя_сценария

Если вы получите какой-то путь, то команда с таким названием существует и вам следует выбрать другое имя. Однако, если полученный результат выглядит примерно вот так:
whereis -b clean
clean:

то, вероятнее всего вы выбрали удачное имя для вашего сценария.

После того, как вы сохраните ваш сценарий, вам следует установить у него атрибут выполняемого файла:
chmod +x clean

Хорошим тоном будет создание в вашей домашнем каталоге подкаталога bin, в котором вы будете хранить ваши сценарии:
cd ~
mkdir bin
mv clean ~/bin

И наконец, вы должны испытать ваш сценарий на работоспособность, прежде чем отдать его cron'у. В каталоге bin напишите:
./clean


Если вы находитесь в другом каталоге напишите:
clean

Если вы используйте интерпретатор C-shell и получите сообщение "Command not found", то напечатайте:
rehash

и повторите запуск сценария.

Теперь мы готовы к созданию файла crontab для того, чтобы cron выполнял наш сценарий, а так же команду find. Зарегистрируйтесь в системе под обычным пользователем. Я регистрируюсь под именем genisis. Теперь пишите:
crontab -e

для использования утилиты crontab в режиме редактирования. Если вы очень шустрый, то вы успеете увидеть сообщение перед запуском редактора vi:
crontab: no crontab for genisis - using an empty one
(crontab: нет файла crontab для пользователя
genisis - используем пустой файл)

Поскольку мы находимся в vi, нажмите клавишу "ESC" и следом символ "i" для входа в режим вставки, затем введите этот текст:
#every morning at 4:32 search and
#destroy all core or dead files
#каждое утро в 4:32 искать и удалять
#все core и dead-файлы
32 4 * * * find / ( -name "*.core" -or
-name "dead.*" ) -print -exec rm -rf {} ;

#run the script that clears the Netscape
#cache every morning at 2:48
#запуск сценария который чистит кеш
#Netscape каждую ночь в 2:48
48 2 * * * ~/bin/clean


(разумеется строки 5 и 6 это на самом деле одна строка).

Обратите внимание, что синтаксис несколько отличается от синтаксиса системного файла crontab, а именно отсутствует поле "кто". Так как этот файл crontab имеет такое же имя как и имя создавшего его пользователя, поле "кто" будет равно имени создавшего файл пользователя (т.е. все указанные в этом файле команды будут выполняться только от имени пользователя, который создал этот файл - прим. переводчика). Когда вы закончите, тщательно проверьте текст на наличие ошибок, а после этого нажмите клавишу "ESC", а затем "wq". Изменения будут сохранены и вы покинете редактор. Если вы введете неверные данные в какие-нибудь поля, утилита crontab сообщит вам об этом и попросит вас заново отредактировать файл. В этом случае скажите "yes" и заново проверьте файл на опечатки. В случае, если все правильно, вы увидите следующую надпись:
crontab: installing new crontab

Когда вы завтра будете проверять свой почтовый ящик, вы увидите два письма от cron'а с результатами деятельности записей в вашем файле crontab. Если ваши команды выполнились успешно, то вы получите нечто похожее:
From genisis@.istar.ca. Thu Sep 14 04:38:31 2000
Date: Thu, 14 Sep 2000 04:38:50 -0400 (EDT)
From: genisis (Cron Daemon)
To: genisis
Subject: cron find / ( -name "*.core" -or
-name "dead.*" ) -print -exec rm -rf {} ;

find: /usr/games/hide: Permission denied
/usr/home/genisis/dead.letter
/usr/home/genisis/netscape.bin.core
find: /var/spool/opielocks: Permission denied
find: /var/cron/tabs: Permission denied
find: /var/games/hackdir: Permission denied
find: /stand/etc: Permission denied
find: /etc/isdn: Permission denied
find: /etc/uucp: Permission denied
find: /root/mail: Permission denied

From genisis@.istar.ca. Thu Sep 14 02:51:36 2000
Date: Thu, 14 Sep 2000 02:52:01 -0400 (EDT)
From: genisis (Cron Daemon)
To: genisis
Subject: cron ~genisis/bin/clean

Clearing Netscape cache...
Exiting....

Теперь несколько финальных замечаний относительно crontab. Если вы хотите увидеть содержимое своего файла crontab, напечатайте:
crontab -l

Если вы хотите изменить этот файл, опять пишите crontab -e.

Только суперпользователь root имеет право видеть, у каких пользователей установлены файлы crontab. Зарегистрируйтесь под пользователем root, и попробуйте написать следующее:
ls /var/cron/tabs
genisis

Вы должны будете увидеть новую запись с именем пользователя, который только что создал свой crontab-файл.

Мы лишь прикоснулись к функциональности файлов crontab. В следующих статьях мы исследуем новые команды и научимся писать более сложные сценарии и обязательно добавим записей в наши файлы crontab.

http://www.onlamp.com/pub/a/bsd/2000/09/27/FreeBSD_Basics.html.

Обновлено: 12.03.2015