Использование UNIX: Часть 6. Автоматизация

Сценарии shell помогают автоматизировать выполнение рутинных операций

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

Мартин Стрейчер, главный редактор, Linux Magazine

Мартин Стрейчер (Martin Streicher) -- главный редактор журнала Linux Magazine. Он имеет степень магистра компьютерных наук Университета Пардью (Purdue University) и с 1982 занимается программированием на языках Pascal, C, Perl, Java и (с недавнего времени) Ruby в UNIX-подобных операционных системах.



29.02.2008

Если посмотреть на экран опытного пользователя UNIX® во время его работы, то можно заметить странные словосочетания, которые он использует в командной строке. Читателю предыдущих статей из цикла "Использование UNIX" (см. раздел Ресурсы) уже знакомы некоторые из странных символов, используемых этим опытным пользователем, например, тильда (~), канал (|), переменные и переадресация (< и >). Он также узнает некоторые UNIX-команды и их комбинации, с которыми работает пользователь.

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

В части 6 цикла статей "Использование UNIX" (см. раздел Ресурсы), будет разобрано, как писать сценарии shell, а также рассмотрены некоторые другие тонкости работы с командной строкой.

Вступление

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

Листинг 1. Простой сценарий shell, который синхронизирует домашний каталог с другими компьютерами в сети
#! /bin/zsh

for each machine (groucho chico harpo)
    rsync -e ssh --times --perms --recursive --delete $HOME $machine:
end

Чтобы использовать листинг 1 в качестве сценария shell, нужно сохранить текст этого листинга в файл с названием simpleprop.zsh и запустить команду chmod +x simpleprop.zsh, чтобы сделать файл исполняемым. Этот сценарий запускается с помощью ./simpleprop.zsh.

Если нужно увидеть, как Z shell раскрывает каждую команду, то надо добавить опцию -x к концу строки, начинающейся с #!, как показано ниже:

#! /bin/zsh -x

Для каждого компьютера: groucho, chico и harpo, сценарий выполняет команду rsync, заменяя каталог $HOME каждого удаленного компьютера на домашний каталог пользователя (например, /home/joe) и заменяя значение переменной $machine именем компьютера.

Как показано в листинге 1, переменные и элементы управления сценария (например, циклы), облегчают создание сценариев и их последующее обслуживание. Если нужно, чтобы сценарий работал с еще одним, дополнительным компьютером, например, zeppo, просто добавьте его в список имен компьютеров. Если нужно изменить команду rsync, скажем, добавить другую опцию, достаточно отредактировать только один образец команды. Как и в классическом программировании, старайтесь избегать приемов "скопировал - вставил" (cut-and-paste) при работе со сценариями shell.


Создание корректных параметров

Некоторым сценариям оболочки для выполнения требуются параметры - динамический список объектов (файлы, каталоги, имена компьютеров). В качестве примера рассмотрим листинг 2, несколько измененный предыдущий листинг, который использует командную строку для ввода имен компьютеров, которые надо синхронизировать.

Листинг 2. Вариант листинга 1 с возможностью задавать имена компьютеров, с которыми надо работать
#! /bin/zsh

for each machine
    rsync -e ssh --times --perms --recursive --delete $HOME $machine:
end

Предположим, листинг 2 сохранен в файл с названием synch.zsh. После этого запускается сценарий zsh synch.zsh moe larry curly, который скопирует домашний каталог пользователя на компьютеры moe, larry, и curly.

Отсутствие списка в строке с foreach не является ошибкой: если не указывать список, foreach будет работать со списком аргументов, введенных с командной строки. Параметры командной строки обычно называют позиционные параметры (positional parameters), потому что позиция параметра в строке команды обычно семантически важна.

Например, листинг 2 можно улучшить таким образом, чтобы выводить сообщения в случае, если сценарий был запущен без ввода параметров. Подобный сценарий будет выглядеть, как в листинге 3.

Листинг 3. Вывод сценарием сообщения в случае, если не было предоставлено позиционных параметров
#! /bin/zsh

if [[ -z $1 || $1 == "--help" ]]
then
    echo "usage: $0 machine [machine ...]
fi

foreach machine
    rsync -e ssh --times --perms --recursive --delete $HOME $machine:
end

Каждая отделенная пробелом подстрока, включая имя вызываемого сценария, становится позиционным параметром. Поэтому команда synch.zsh имеет только один позиционный параметр: $0. Команда synch.zsh --help имеет два позиционных параметра: $0 и $1, где $1 является строкой --help.

Таким образом, в листинге 3 говорится: "Если отсутствует первый позиционный параметр (оператор -z осуществляет проверку на пустую строку) или (обозначено символом ||) первый параметр равен '--help', следует вывести служебное сообщение". При написании сценария shell нужно предусмотреть в сценарии вывод сообщений для пользователя. Это поможет другим пользователям (и даже автору сценария) узнать, как использовать данный сценарий.

Фраза [[ -z $1 || $1 == "--help" ]] является условием оператора if, также можно использовать это условие как команду и комбинировать его с другими командами для осуществления контроля за выполнением программного кода. Рассмотрим листинг 4. Этот листинг использует комбинацию условий и других команд, чтобы перечислить все выполняемые команды, хранящиеся в переменной $PATH (каталоги, которые должна просмотреть shell для обнаружения команды).

Листинг 4. Список команд в $PATH
#! /bin/zsh

directories=(`echo $PATH | column -s ':' -t`)

for directory in $directories
do
  [[ -d $directory ]] || continue

  pushd "$directory"

  for file in *
  do
      [[ -x $file && ! -d $file ]] || continue
      echo $file
  done

  popd
done | sort | uniq

Этот сценарий не такой уж и большой, его стоит рассмотреть подробно:

  1. Первая строчка сценария - directories=(`echo $PATH | column -s ':' -t`) - создает массив определенных каталогов. Чтобы создать массив в zsh, поместите элементы массива в круглые скобки, например, directories=(...). В этом случае элементы массива генерируются путем разбиения $PATH на подстроки по двоеточиям (column -s ':') для получения списка каталогов, элементы которого отделены друг от друга пробелами (параметр -t, принадлежащий column).
  2. Сценарий пытается перечислить исполняемые файлы в каждом каталоге из полученного списка. Шаги с третьего по шестой описывают этот процесс.
  3. Строка [[ -d $directory ]] || continue является примером замкнутого условия (short-circuiting command). Проверка условия прекращается, "как только оно замкнется", т.е. примет конечное значение.

    Например, фраза [[ -d $directory ]] || continue использует логическое ИЛИ (OR) (||). В этом случае выполняется первая команда, а вторая команда выполняется, только если выполнение первой команды было неудачным. Поэтому, если запись в $directory существует и является каталогом (оператор -d), проверка считается успешной, и на этом проверка условия заканчивается, и команда continue, чтобы пропустить обработку текущего элемента, не выполняется.

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

    В закороченной (short-circuiting) команде на основе логического И (AND) (&&) выполняется первая подкоманда, а затем, только если первая подкоманда была выполнена успешно, выполняется вторая подкоманда.

  4. Команды pushd и popd используются для смены текущего каталога следующим каталогом из массива перед обработкой файлов в этом каталоге (pushd) и на обратную смену каталогов, когда предыдущий каталог вновь становится текущим после окончания обработки каталога (popd). Применение стека каталогов будет полезным при написании сценариев для отслеживания вашего местоположения в файловой системе.
  5. Внутренний цикл for перебирает все файлы в текущем рабочем каталоге, "звездочка" (*) означает, что надо выбрать все содержимое каталога, а затем проверяет каждый элемент каталога на предмет того, является ли он файлом. Строчка [[ -x $file && ! -d $file ]] || continue расшифровывается как "Если $file существует, является исполняемым и не является каталогом, тогда обработать его; в противном случае, continue."
  6. В итоге, если все предшествующие условия были выполнены, выведется имя файла при помощи команды echo.
  7. А что значит последняя строчка сценария? Информацию, являющуюся результатом работы сценария, можно отправлять в другие UNIX-команды, в общем случае shell интерпретирует сценарий как команду. Поэтому информация, выводимая сценарием, передается в sort, а затем в uniq, чтобы получить список неповторяющихся команд, имеющихся в переменной $PATH.

Если сохранить листинг 4 как исполняемый файл listcmds.zsh, результаты работы данного сценария будут похожи на показанные ниже:

$ ./listcmds.zsh
[
a2p
ab
ac
accept
accton
aclocal

Закороченные команды short-circuiting очень полезны при разработке сценариев. Они сочетают в себе одновременно условия и действия. И поскольку каждая UNIX-команда возвращает код состояния, по которому видно, успешно ли выполнилась команда, то в качестве условия можно использовать любую команду, а не только условные операторы. Команды UNIX возвращают ноль (0) в случае успешного завершения операции и ненулевое значение в случае неудачного выполнения операции, где ненулевое значение зависит от того, какая произошла ошибка при выполнении команды.

Например, pushd и popd можно исключить из листинга 4 в случае, если строка [[ -d $directory ]] || continue будет заменена на строку cd $directory || continue. Если команда cd успешно выполнится, она вернет ноль, и проверка логического ИЛИ (OR) будет немедленно завершена. Однако, если cd не выполнится, она вернет ненулевое значение, и оценка условия продолжится, что приведет к выполнению continue.


Перед удалением архивируйте данные

Современные оболочки UNIX - bash, ksh, zsh - предлагают для использования множество элементов управления и операций для создания сложных сценариев. Так как все UNIX-команды могут передавать данные от одной команды к другой, создание сценариев shell по функциональным возможностям сравнимо с классическими языками программирования, такими как C или Perl.

Можно использовать сценарии, чтобы автоматизировать выполнение любой пользовательской или системной задачи. Сценарии могут осуществлять мониторинг различных параметров, архивировать, обновлять, загружать и преобразовывать данные. Сценарий может представлять собой как одну строчку кода, так и достаточно сложный и большой программный код. В сценарии можно запрограммировать любую задачу, как большую, так и маленькую. Если заглянуть в каталог /etc/init.d, там можно найти большое количество готовых сценариев shell, которые запускают различные службы при запуске компьютера. Если создать очень полезный сценарий, то его можно установить как системную утилиту. Все, что для этого надо, - это поместить сценарий в $PATH каждого пользователя.

Создадим утилиту для закрепления полученных знаний. Сценарий myrm заменит аналогичную системную утилиту rm. Вместо того чтобы сразу удалить файл, myrm копирует файл в архив и присваивает архиву уникальное имя, по которому его можно найти позже, а затем удаляет исходный файл. Сценарий myrm функционален, но вместе с тем прост, и впоследствии в нем можно реализовать дополнительные возможности. Вместе с этим сценарием можно написать расширенную версию сценария unrm (un-remove) в качестве дополнения к сценарию myrm. (В Интернете можно найти различные реализации unrm.)

Сценарий myrm приведен в листинге 5.

Листинг 5. Простая утилита для создания резервной копии файла, прежде чем этот файл будет удален из файловой системы
#! /bin/zsh

backupdir=$HOME/.tomb
systemrm=/bin/rm

if [[ -z $1 || $1 == "--help" ]]
then
  exec $systemrm
fi

if [[ ! -d $backupdir ]]
then
  mkdir -m 0700 $backupdir || echo "$0: Cannot create $backupdir"; exit
fi

args$=$( getopt dfiPRrvw $* ) || exec $systemrm

count=0
flags = ""
foreach argument in $args
do
  case $argument in
    --) break;
        ;;

     *) flags="$flags $argument";
        (( count=$count + 1 ));
        ;;
  esac
done
shift $(( $count ))

for file
do
  [[ -e $file ]] || continue
  copyfile=$backupdir/$(basename $file).$(date "+%m.%d.%y.%H.%M.%S")
  /bin/cp -R $file $copyfile
done

exec $systemrm $=flags "$@"

Хотя этот сценарий достаточно простой, в нем присутствуют некоторые элементы, которые не рассматривались ранее. Давайте сначала обсудим их, а затем разберем сценарий в целом.

  1. При запуске команды типа cp или ls оболочка создает для нее новый процесс, а затем ждет, пока этот процесс закончится, чтобы продолжить работу. Команда exec также запускает команду, но не создает для нее новый процесс, а повторно использует уже запущенный процесс (это может быть процесс оболочки или процесс сценария) для выполнения команды. Проще говоря, exec использует текущий процесс для запуска новой задачи. В представленном сценарии, exec немедленно завершает работу сценария и приступает к выполнению поставленной задачи.
  2. UNIX-утилита getopt ищет в позиционных параметрах указанные вами параметры. Эта утилита осуществляет поиск опций -d, -f, -i, -P, -R, -r, -v и -w при помощи списка dfiPRrvw. Если в списке обнаружится какая-либо другая опция кроме указанных выше, утилита getopt аварийно завершит свое выполнение. В случае успешного выполнения, getopt возвращает строку, состоящую из опций, заканчивающуюся специальной комбинацией символов --.
  3. Команда shift выполняет удаление позиционных параметров слева направо. Например, если строка команды была myrm, -r -f -P file1 file2 file3, shift 3 удалит $0, $1 и $2 или -r, -f и -P соответственно. file1, file2 и file3 будут перенумерованы как $0, $1 и $2.
  4. Оператор case работает так же, как и его реализации в традиционных языках программирования: он сравнивает свой параметр с каждым образцом в списке; когда совпадение будет найдено, будет выполнен соответствующий код. Как и в оболочке, * (звездочка) означает соответствие чему угодно и может быть использована как вариант действия по умолчанию, если никакого другого совпадения не было найдено.
  5. Символ $@ говорит, что эти действия надо провести со всеми оставшимися позиционными параметрами.
  6. Оператор zsh, $=, разбивает слова, ориентируясь по пробелам. Оператор $= полезен, когда есть длинная строка и нужно разбить эту строку на слова. Например, если переменная x содержит строку '-r -f', которая состоит из одного слова в пять символов, то после выполнения команды $= x станет переменной с двумя отдельными словами - -r и -f.

Учитывая комментарии выше, теперь можно разобрать код полностью. Давайте разберем код листинга по блокам:

  • Первый блок инициализирует переменные, которые будут использоваться далее по ходу сценария.
  • Следующий блок кода должен быть знакомым читателю: он выводит сообщение в случае, если на вход не было передано аргументов. Почему exec можно считать почти абсолютной заменой исходной утилиты rm? Если назвать этот сценарий rm и поместить его в $PATH, он может использоваться как замена /bin/rm. Так как неверное действие в отношении сценария также является неверным и в отношении /bin/rm, то сценарий позволяет /bin/rm генерировать сообщения.
  • Следующий блок кода создает каталог для резервного хранения, если он еще не существует. Если выполнение mkdir будет неудачным, сценарий прекратит свое выполнение с соответствующим сообщением об ошибке.
  • Следующий блок кода ищет опции с тире в списке позиционных параметров. Если getopt будет выполнен успешно, $args будет содержать список опций. Если getopt не найдет требуемых опций и завершит свое выполнение, то сценарий выведет сообщение об ошибке и завершит свое выполнение.
  • Следующий блок кода собирает все опции, предназначенные для rm, в строку. Соединение этих опций в одну строку остановится, когда getopt дойдет до особой опции --. Оператор shift удалит все обработанные параметры из списка параметров, оставляя для последующей обработки список файлов и каталогов.
  • В блоке, который начинается с for file, каждый файл или каталог копируются для длительного хранения в архив. Точная копия каждого каталога (опция -R) копируется в резервное хранилище, причем к имени файла добавляется его дата и время создания, чтобы быть уверенным, что копия уникальна и не перезапишет предыдущую архивную запись с таким же именем.
  • Наконец, файл или каталог удаляются с учетом тех самых опций командной строки, которые были переданы сценарию.

Если потребуется файл или каталог, который только что был удален (возможно, случайно), то можно будет взять из архива старую копию!


Заключение

Чем дольше работаешь с UNIX, тем больше нравится писать сценарии shell. Эти сценарии сохранят время и силы, которые нужны, чтобы раз за разом вводить длительные последовательности команд, и помогут избежать возможных ошибок при перепечатывании команд. В Интернете много полезных сценариев для разных целей, созданных другими пользователями. Впрочем, вскоре вы тоже будете выкладывать в Интернет свои собственные разработки.

Ресурсы

Научиться

Получить продукты и технологии

  • Z shell: c домашней страницы проекта Z shell можно загрузить последнюю версию Z shell.(EN)
  • IBM trial software: ознакомительные версии программного обеспечения для разработчиков, которые можно загрузить прямо со страницы сообщества developerWorks.(EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=292441
ArticleTitle=Использование UNIX: Часть 6. Автоматизация
publish-date=02292008