Журнал LinuxFormat - перейти на главную

LXF79:Hardcore Linux

Материал из Linuxformat
Перейти к: навигация, поиск

Командная строка: секреты Bash

«Это кронтаб. Детки роются в кронтабе». Примерно так бормочет Пол Хадсон (Paul Hudson), весь этот месяц доказывавший своё мужество в неравной схватке с управлением заданиями, навигацией по каталогам Linux и переменными окружения.

Тропа гуру командной строки со всех сторон стеснена недочетами графических инструментов и тиранией невежества. Но если сложности прошлого урока не сбили вас с пути, то осталось изучить последние несколько строк катехизиса команд, и вы получите право хвастаться перед окружающими – и согласитесь, это превесело: быть местным парнем-всезнайкой.

В последнем выпуске мы рассмотрели специфические команды Unix-окружения, вместе с некоторыми, встроенными в Bash. На этот раз я сосредоточусь на том, что предоставляет Bash и как работает система управления заданиями в Unix. Для этого вам могут понадобиться привилегии суперпользователя.

Содержание


Основы заданий в Bash

Откройте окно терминала и введите такую команду:

yes > /dev/null

Команда yes выводит на экран (или в /dev/null, как в нашем примере) ‘y’, пока вы не прервёте ее (некоторым потребуется время, чтобы понять, в чем ее полезность). Предоставленная сама себе, команда будет выполняться вечно, так что оставим её транжирить циклы процессора, а сами откроем новый терминал и запустим в нём top. Вы должны увидеть, что yes использует до 100% процессорного времени. Затем оставьте окно с top запущенным и вернитесь в терминал с yes. Вам пора уже знать, что нажатие Ctrl+C посылает текущему процессу сигнал SIGINT и обычно приводит к его прерыванию. Можете также нажать Ctrl+\, чтобы послать процессу сигнал SIGQUIT, сигнал немедленно завершит процесс и создаст дамп памяти (core dump). Впрочем, куда важнее, что вы можете использовать Ctrl+Z, чтобы послать SIGSTOP.

Большинство сигналов в Linux отправляются программе и обрабатываются ею. Например, нажатие Ctrl+C посылает SIGINT процессу, а тот его обрабатывает. Процесс может проигнорировать сигнал (в этом случае Ctrl+C ничего не делает), а может выполнить некоторые действия перед завершением работы. Но два сигнала – SIGSTOP и SIGKILL – процессу не отправляются. Вместо этого они передаются непосредственно ядру, потому что требуют внешнего воздействия на процесс. SIGKILL вы уже должны знать по команде kill -9; это сигнал, говорящий: «Любой ценой уничтожь этот процесс». SIGSTOP же говорит: «Хочу временно приостановить этот процесс. Не прибить его, но и не давать ему процессорного времени».

Давайте попробуем применить это к команде yes. Выбрав данный терминал, нажмите Ctrl+Z. Вы должны увидеть такой вывод:

[1]+ Stopped               yes > /dev/null

Это означает, что наша команда (yes > /dev/null) остановлена, и на неё можно сослаться как на задание №1. Терминал теперь в ваших руках, так что попробуйте запустить fg (foreground) для реанимации yes. Внимание: отправка SIGSTOP и запуск fg похожи на нажатие паузы на вашем CD-плейере, поскольку процесс на самом деле не прекращает существование, а просто временно приостанавливает работу.

Итак, yes снова работает. Нажмите Ctrl+Z, чтобы опять приостановить процесс, но теперь введите bg, чтобы Bash возобновил его работу и оставил его в фоновом режиме – как будто вы запустили yes > /dev/null &. Теперь yes работает в фоновом режиме, и Ctrl+Z не сможет остановить его. Вместо этого мы должны послать сигнал SIGSTOP непосредственно процессу, используя команду:

kill -SIGSTOP %1

%1 означает, что Bash должен послать сигнал заданию №1, т.е. нашей команде yes. Если вы опасаетесь, что не упомните все цифровые идентификаторы, самое время познакомиться с jobs. У этой команды нет интересных параметров, но она возвращает список всех созданных нами заданий:

paul@Susannah:~$ jobs
[1]-          Running     yes > /dev/null &
[2]           Stopped     top
[3]+          Stopped     du /

В левом столбце перечислены все запущенные задания, и вы сможете перезапустить любое из них, просто набрав % с последующим номером, например, %1 перезапустит команду yes. В истинном Unix-стиле добавление амперсанда (%1 &) перезапустит задание №1 в фоновом режиме.

Вы, вероятно, недоумеваете, почему задание №1 имеет знак «минус» после номера, задание №2 не имеет никакого знака, а №3 – знак «плюс». Так Bash отмечает, что рассматривается в качестве текущего (+) и предыдущего (-) задания, и вы можете использовать это для краткости. Например, если перевели команду yes в фоновый режим, вы можете воскресить её, набрав %+ (или даже %%). Для ссылки на предыдущее задание используйте %-.

Автоматизация работы

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

Первый – команда at, позволяющая запланировать программу на запуск в заданное время. Изначально она проектировалась для одноразовых заданий, которые вы хотели выполнить в определённое время (но не сейчас). Второй инструмент – batch, он подобен at, с тем исключением, что время вы не задаёте – задание просто запустится, когда система не загружена. Лучше всего это подходит для ситуаций, когда ресурсы компьютера используются кем-то ещё, а вам нужно на что-то надолго отвлечься. Последний инструмент – Cron, этот планирует задания для регулярного выполнения.

At и batch – милые, удобные инструменты, в то время как Cron выглядит слегка пугающе. Так что давайте начнём с того, что попроще…

At – это где?

Команда at обычно работает интерактивно, то есть вы сначала вызываете программу, указав время, когда задание должно выполниться, затем вводите свои команды и нажимаете Ctrl+D, чтобы сохранить задание. Вот пример «диалога» с at:

paul@Susannah:~$ at midnight
at> du / > /home/paul/diskusage
at <EOT>
job 4 at 2006-03-24 00:00

В первой строке я запускаю at и указываю полночь (midnight) как время старта для задания. При этом at запустится, и появится приглашение at>, показывающее, что можно вводить содержимое задания. Задание, которое я хочу выполнить в полночь – выяснить, сколько дискового пространства используется на моём компьютере, так что я запускаю du и перенаправляю её вывод в файл в моём домашнем каталоге. <EOT> – это я нажал Ctrl+D, что приводит к сохранению задания, выводу его номера и сообщению, когда оно будет выполнено (сегодня в полночь).

Узнать, какие задания уже поставлены в очередь, вы можете, набрав atq, которая распечатает что-то наподобие

4 2006-03-24 00:00 a                 paul

Первое, второе и четвёртое поля – это соответственно номер задания, время, когда оно будет выполнено, и кто его создал, но о третьем поле нужно сказать особо: это приоритет задания. В Linux можно выбрать много очередей заданий, и чем дальше буква от начала алфавита, тем ниже приоритет. Очередь «a» имеет наивысший приоритет, и будет исполнена с нормальным для пользователя значением nice (то есть так быстро, как только сможет).

Если вы дождётесь полуночи, ваше задание будет выполнено, как и планировалось. Но если вы передумаете, используйте команду atrm, чтобы удалить задание:

atrm 4

Есть несколько способов указать время в at, и midnight – лишь один из них. Из предопределённых есть tomorrow (завтра), noon (полдень) и teatime (4 часа вечера), но вы можете указывать и точное время, например, 16:00 (те же 4 вечера) или комбинировать эти значения (16:00 tomorrow). Простейший способ – указывать относительное время, используя now (сейчас), например, так:

at now + 5 minutes
at now + 3 hours
at now + 4 weeks

Если время выполнения для вас не имеет значения, забудьте про at и используйте batch. Различие заключается в том, что batch начнёт выполнять ваши задания сразу же, как только загрузка системы опустится ниже 0,8 (т.е. машина будет не слишком занята). Синтаксис намного проще, поскольку не нужно указывать время: просто наберите batch, нажмите Enter, добавьте свои команды и нажмите Ctrl+D, чтобы сохранить задание.

Введя atq, вы увидите ваше задание в очереди «B», что означает запуск с более низким приоритетом, чем у других заданий и большинства программ в системе. По этой причине ваше задание стартует только тогда, когда система бездействует, но если оно запустится, а в следую щую секунду машину потребует другая работа, ваше задание тихонько переберется в фоновый режим и отдаст ресурсы процессора. Оно не останавливается, но из-за более низкого приоритета получит намного меньше процессорного времени.

Никто не любит вводить одну и ту же команду снова и снова, так что если вы хотите, чтобы at или batch читали ваши задания из файла, просто используйте -f имяфайла перед указанием времени, например, так:

at -f myjob.job tomorrow
batch -f myjob.job

Нет ничего прекраснее, чем заставить ваш компьютер делать кучу работы за вашей спиной, полностью автоматически, пока вы играете в UT. Волшебной программой, осуществляющей это, является Cron, демон времени. Каждую минуту каждого часа, ежедневно, на протяжении всего года, миллионы демонов Cron по всему миру проверяют, нужно ли что-то сделать, затем, разочаровавшись, снова бездействуют 60 секунд. Не позволяйте вашему демону скучать – пусть он немного порезвится!

Вот работа для Cron

Задания Cron можно разделить на два типа: пользовательские и системные. Большинство дистрибутивов Linux поставляются с Cron, способным на обе роли, то есть каждый может назначать свои персональные задания, и сама система тоже будет регулярно что-нибудь запускать. Вам потребуются права суперпользователя, чтобы изменять системные задания, поскольку они отвечают за важные вещи – обновление базы данных locale, генерирование базы данных man, скачивание и применение обновлений безопасности, и т.д. Многие из них, вероятно, уже есть в вашем дистрибутиве, и мы довольно скоро их рассмотрим.

Но сначала рассмотрим пользовательский Cron – регулярные задания, назначаемые пользователем для выполнения повседневных задач. Например, часто требуются ночные сборки программного проекта. Если вы тестируете KDE и приближается дата выпуска, вы, вероятно, захотите скачивать новый релиз каждый день, компилировать его и тестировать, выискивая ошибки. Загрузка выполняется довольно просто: возьмите Konstruct, отредактируйте конфигурационный файл так, чтобы выполнялась проверка последней версии исходного кода в Subversion, и пересоберите. Но сборка KDE может потребовать более четырёх часов, в зависимости от числа изменений и скорости вашей машины. Вот и работа для Cron!

Введите crontab -e, чтобы отредактировать ваш персональный crontab (так называется файл Cron со списком ваших заданий). Если у вас его ещё нет, он будет создан. Теперь – самая сложная часть: хитрый формат файла crontab. Наберите в нём такую строку:

* * * * * du -h / > /home/paul

Вместо paul укажите ваше собственное имя [пользователя]. Часть du и последующие символы – это команда, которую Cron должен выполнить, а все эти звёздочки определяют время, когда она должна выполняться. Это смешнее всего! По порядку:

  • Минута (0-59)
  • Час (0-23)
  • День (1-31)
  • Месяц (1-12)
  • День недели (0-7, как 0, так и 7 соответствуют воскресенью)

Звёздочка означает «все значения», так что «* * * * *» означает «каждую минуту каждого часа ежедневно каждый месяц, независимо от дня недели». Следующее задание будет выполняться каждую субботу в полдень:

 0 12 * * 6 du -h / > /home/paul

Cron перечитывает свои конфигурационные файлы раз в минуту, но не сразу выполняет все обнаруженные задания. То есть, если сейчас без десяти секунд полночь и вы установите задание на полночь, оно будет выполнено через 24 часа + 10 секунд. [Точнее, при первом после добавления задания запуске выполняется подготовка заданий, а при втором (через минуту) – их выполнение, – прим. перев.]

Помимо указания точного времени для настройки простых заданий, вы можете определить и более сложные выражения, используя дефисы и запятые. Например, можно создать четыре задания Cron для запуска сценария в 11, 12, 13 и 14 часов, а можно просто записать задание таким образом:

0 11-14 * * * du -h / > /home/paul

Запятые позволяют указывать конкретные значения вместо диапазона, так что мы можем сделать то же самое другим способом:

0 11,12,13,14 * * * du -h / > /home/paul

Но и без этого вы можете создать столько заданий, сколько захотите – просто вводите их, каждое в свою строку, в ваш crontab. Просмотреть текущий crontab позволяет команда crontab -l, но она просто распечатает то же самое, что вы увидите при редактировании файла, так что проку от нее мало.

Корни Cron

Раз уж вы освоили мощь Cron с точки зрения пользователя, пора браться за более «продвинутую» вещь: редактирование системных файлов crontab. Да, их несколько – во многих системах есть скрипты, содержащие почасовые, ежедневные, еженедельные и даже ежемесячные задания. Откройте в своём любимом текстовом редакторе файл /etc/crontab, и вы увидите нечто вроде

17 * * * * root run-parts --report /etc/cron.hourly
25 6 * * * test -x /usr/sbin/anacron || run-parts --report /etc/cron.daily
47 6 * * 7 test -x /usr/sbin/anacron || run-parts --report /etc/cron.weekly
52 6 1 * * test -x /usr/sbin/anacron || run-parts --report /etc/cron.monthly

То есть часовые задания выполняются в 17 минут каждого часа, ежедневные – каждое утро в 6:25, еженедельные – по воскресеньям в 6:47 и ежемесячные – в 6:52 первого числа месяца. На вид время выполнения задано «с потолка», но в нем есть свой смысл – большинство заданий выполняется утром, когда нагрузка минимальна, и они запускаются в разное время, чтобы избежать столкновения между собой.

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

Если вы не сталкивались с этой утилитой раньше, то поясним, что run-parts выполняет все программы в заданном каталоге и может дополнительно предоставлять отчёт (--report) об этом выполнении.

Она идеальна для заданий Cron, поскольку может разделять их по каталогам /etc/Cron.hourly, /etc/Cron.daily и так далее. Если вы хотите добавить собственное задание в список ежечасных, просто скопируйте исполняемый сценарий в /etc/Cron.hourly и предоставьте run-parts сделать всё остальное.

Помимо настройки системных заданий, права root дают нам ещё одну привилегию: мы можем решать, кому позволить, а кому запретить планирование заданий. Каждый пользователь, имеющий право использовать at и batch, должен быть указан в файле /etc/at.allow. Но если вы попытаетесь открыть этот файл (как root), то, вероятно, обнаружите, что его вообще не существует. Это потому, что по умолчанию каждый пользователь рассматривается как имеющий право работать с at и batch, если он явно не указан в файле /etc/at.deny [который по умолчанию присутствует, – прим. перев.]. В представленной таблице разъясняется, как работают права.

Как at.allow и at.deny распоряжаются правами на задания

at.allow at.deny Можете ли вы использовать at/batch?
Не существует Не существует Нет
Не существует Существует, и вы в нём Нет
Не существует Существует, но вас там нет Да
Существует, но пуст Существует, и вы в нём Нет
Существует, но пуст Существует, но вас там нет Нет
Существует, и вы в нём Существует, но вас там нет Да
Существует, но вас там нет Существует, и вы в нём Нет
Существует, и вы в нём Существует, и вы в нём Да

Учитесь запоминать

Вспомните лекции по информатике. В частности, абстрактные типы данных. Вы должны помнить их – стеки, очереди и т.д. Если же вы спали на лекциях (думаю, большинство из нас так и делало), напомню, что стеки – это массивы типа «Последний пришёл – первый вышел», то есть поместив в стек элемент A, затем B, а затем C, извлечь их оттуда вы сможете в порядке C, B, A. Элемент A не достать, пока не извлекут элементы C и B.

Всё это я говорю потому, что мы частенько рассматриваем навигацию по каталогам Linux как совершенно произвольную и сложную для отслеживания операцию, хотя на самом деле можно трактовать наши перемещения как стек. Bash выполняет всё это для нас с помощью команд pushd и popd, которые изменяют текущий каталог. Вы помещаете (push) каталоги в стек Bash (перемещаясь в них и добавляя их к списку сохранённых каталогов), и затем извлекаете (pop) их из стека (удаляя его и возвращаясь в предыдущий каталог).

Это лучше продемонстрировать на примере:

paul@Susannah:~$ pushd /etc/init.d
/etc/init.d ~
paul@Susannah:/etc/init.d$ pushd /var/log
/var/log /etc/init.d ~
paul@Susannah:/var/log$ pushd /usr/local/bin
/usr/local/bin /var/log /etc/init.d ~
paul@Susannah:/usr/local/bin$ popd
/var/log /etc/init.d ~
paul@Susannah:/var/log$ popd
/etc/init.d ~
paul@Susannah:/etc/init.d$ popd
~
paul@Susannah:~$

Первоначально я нахожусь в моём домашнем каталоге, но хочу перейти в /etc/init.d. Если я выполню cd /etc/init.d, я тут же потеряю путь, где я был до этого, поэтому я использую pushd /etc/init.d, которая делает текущим /etc/init.d, но сохраняет мой предыдущий каталог в стеке каталогов. Обратите внимание, что Bash выводит на экран мой стек – сейчас это /etc/init.d ~, что означает: «Ты сейчас в /etc/init.d, а до этого был в ~».

Теперь я снова использую pushd /var/log, чтобы сменить каталог, и Bash добавит его в список. Далее, я таким же образом перехожу в /usr/local/bin, и теперь у нас четыре элемента в стеке каталогов. Пора воспользоваться popd. Каждый раз, когда я вызываю popd, верхний элемент (каталог слева в стеке) удаляется, и я возвращаюсь в предыдущий каталог. Из /usr/local/bin я перехожу в /var/log, затем в /etc/init.d, и, наконец, назад в ~, мой домашний каталог – и стек опять пуст.

Есть ещё несколько трюков, которые можно выполнить с помощью pushd и popd. Во-первых, простой ввод pushd будет переключать вас между двумя верхними каталогами в стеке, перемещая вас туда и обратно без изменения остальной части стека. С помощью popd вы можете удалить каталоги из стека, введя popd +X, где X – элемент стека, подлежащий удалению. Нумерация элементов в стеке начинается с нуля, так что самый верхний каталог (текущий) будет иметь индекс 0, следующий – 1, и т.д. Таким образом, ввод popd +2 удалит из стека третий элемент.


Сохранение окружения

Bash обеспечен полным комплектом переменных окружения – введите set, и вы поймёте, о чем я говорю. Многие из них устанавливаются автоматически, такие как USER (ваше имя пользователя), LINES (число строк, отображаемых вашим терминалом), HOME (ваш домашний каталог) и т.д. Некоторые переменные окружения уже установлены, но часто меняются, например, PATH, сохраняющая все каталоги, где будет выполняться поиск исполняемых файлов. Другие переменные окружения полностью определяются пользователем – вы можете устанавливать собственные значения, чтобы хранить интересующие вас данные.

Изменение таких переменных, как PATH, бывает весьма полезным, особенно учитывая, что Bash различает операции «установка переменной для одной команды» и «установка переменной для всех команд в текущем сеансе». Попробуйте сами:

cp /bin/mount ~/date
PATH=. date
date

Здесь /bin/mount копируется в ваш домашний каталог и переименовывается в date. Но date – это исполнимая программа в /bin, так что второй командой мы говорим: «Установи наш путь только на текущий каталог (так что ~/date будет читаться, а /bin/date нет) и затем исполни команду date». В результате мы запустим нашу копию mount. Но если вы выполните следующую команду, date, вы увидите нормальный вывод программы date, поскольку переменная PATH вернётся к своему обычному значению. Если вы хотите установить переменную на весь сеанс, используйте export, например, так:

export PATH=/path/to/someplace:$PATH

Эта команда добавит /path/to/someplace в начало списка каталогов в PATH, дописав оставшиеся значения в конец.

Некоторые дистрибутивы (самый злостный преступник, пожалуй, SUSE) доводят использование переменных окружения до крайности и записывают в них сложные функции, загромождающие вывод простой и милой команды set, но не пугайтесь: воспользуйтесь графическим терминалом, установите буфер вывода побольше и прокручивайте окно назад, пока не найдёте то, что вас интересует.

Персональные инструменты
купить
подписаться
Яндекс.Метрика