Как в Mac OS самостоятельно создавать командные сценарии


Оболочка UNIX — это не просто среда, посредством которой пользователь взаимодействует со своим компьютером Mac, давая команды через Terminal. В действительности это еще и простой язык программирования, с помощью которого вы можете автоматизировать практически любую задачу, которую вам требуется выполнять достаточно часто.Большинство непосвященных, впервые узнав об оболочках UNIX (UNIX shells), удивляются тому, как кто-то еще может пользоваться таким архаичным методом взаимодействия с компьютером. Обычно они недоуменно спрашивают у юниксоидов, неужели те не слышали о графических пользовательских интерфейсах (GUI)? Однако оболочки предоставляют массу возможностей, отсутствующих в графических интерфейсах. К числу наиболее существенных преимуществ оболочек UNIX относятся символы шаблона (wildcards), командные сценарии (scripting) и автоматизация (automation).

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

1$ ls –l *.jpg

Обозначение *.jpg представляет собой сокращение, позволяющее указать команде ls на необходимость вывести список всех файлов, имеющих расширение .jpg, хранящихся в текущем каталоге, с указанием их размеров. Например, для поиска в текущей папке файлов с расширением .doc, содержащих строку Devanshu Mehta, можно дать следующую команду:

1$ grep Devanshu *.doc

Эта команда выведет список всех файлов, содержащих указанную строку (листинг ниже).

1234// Результат поиска файлов документов, содержащих указанную строкуBinary file BBoAH-04-Commandline.doc matchesBinary file BBoAH-06-Network-file.doc matchesBinary file BBoAH-14-Multimedia.doc matches

Команда grep хорошо работает с двоичными файлами, но еще лучше она работает с текстовыми файлами, включая .txt-, .html- и .xml-файлы.

Grep — это программа, выполняющая поиск указанной строки по файлам. В действительности она использует довольно сложные комбинации символов, в том числе и символов шаблона, для описания искомой строки. Использование регулярных выражений (regular expressions) позволяет описывать искомые строки с использованием альтернативных символов, а также классов букв и цифр. Например, описание [sS]hell позволяет найти вхождения shell и Shell, а описание [a-z]+ дает возможность поиска любых последовательностей из одной или нескольких букв на нижнем регистре клавиатуры. Чтобы прочесть описание этой команды, введите из командной строки следующую команду: man grep.

При использовании комбинаций команд grep и find можно получить результаты, аналогичные тем, что выдает Spotlight, с тем лишь недостатком, что наилучшим образом данный метод работает с текстовыми файлами. Однако есть у данного метода и свое преимущество — оно заключается в том, что таким образом можно задавать гораздо более сложные искомые строки, нежели при работе через Spotlight. Вот пример использования команды find, результатом которого будет вывод списка всех файлов, содержащихся в текущем каталоге (включая файлы во вложенных каталога), содержащих упоминание каких-либо стандартов ISO:

1$ find . -type f -exec grep «ISO [0-9]+» {} ; -print

Программа find производит сортировку по каталогам, выделяет файлы и передает их программе grep, которая ищет в них строку ISO, за которой следует одна или несколько цифр. Если вам кажется, что каждый раз вводить такую строку проблематично и неудобно, то вот вам хорошая новость. На самом деле делать это не обязательно. Я использую эту команду постоянно, но не ввожу ее с клавиатуры. Хотите знать, как это делается? Далее я расскажу, как этого добиться.

Переменные и псевдонимы

В среде оболочки есть два способа запоминания командных строк: псевдонимы (aliases) и переменные (variables). Псевдоним — это сокращенное имя команды, которая используется постоянно. Например, я создаю папки для хранения текстов книг, над которыми я работаю, и храню их в каталогах, которые сортируются по году выхода книг. Этот подход очень удобен, но постоянно вводить строки наподобие ~/ tech/08/oreilly/applehacks/draft довольно неудобно. Поэтому я воспользовался следующей командой для создания псевдонима:

1$ alias ah=’cd ~/tech/08/oreilly/applehacks/draft’

Теперь, если мне требуется перейти в каталог ~/tech/08/oreilly/applehacks/draft с клавиатуры, я просто использую псевдоним ah, и эта команда мгновенно перемещает меня в нужный каталог. Что еще удобнее, я могу сохранить эту строку в специальном файле (без символа $, который, как уже говорилось ранее в этой главе, представляет собой приглашение командной строки), который называется .bash_profile и хранится в моем домашнем каталоге. Этот файл содержит команды, выполняющиеся при запуске оболочки bash. Благодаря этому псевдоним будет создаваться каждый раз при запуске Terminal или любой другой программы, которая запускает bash при старте.

Кроме того, оболочки позволяют определять переменные (variables), которые могут замещать любую часть команды. Дело в том, что при всем своем удобстве, псевдонимы имеют и некоторые ограничения. Например, для получения еще более гибких возможностей, я выполняю команды, приведенные в листинге.

123// Пример, иллюстрирующий использование переменных$ export WORK=~/tech/08/oreilly/applehacks/draft$ alias work=’cd $WORK’

Теперь я могу ввести из командной строки следующую команду:

1$ work

В результате исполнения команды work я мгновенно попадаю в каталог, отведенный для работы над текущим проектом (в данном случае текущим проектом была, как вы сами понимаете, книга, которую вы держите в руках). Значок $ в комбинации $WORK указывает оболочке, что строка, следующая непосредственно за ним, представляет собой имя переменной, и пользователю (в данном случае — мне) требуется получить значение этой переменной. Я могу обновлять значение переменной $WORK, и каждый раз при вводе из командной строки команды work я каждый раз буду мгновенно перемещаться в каталог, путь к которому указан в качестве значения переменной.


Оболочки определяют ряд переменных окружения, благодаря которым система может работать. Например, $HOME — это путь к вашему домашнему каталогу, который обычно выглядит примерно так: /Users/username обычно обозначается значком ~. Ваш текущий рабочий каталог задается переменной окружения $PWD. При использовании команды cd вы изменяете значение этой переменной. Еще одна важная переменная окружения — это переменная $PATH. Она задает список каталогов, в которых система ищет команды, которые вы вводите. Попробуйте ввести из командной строки следующую команду:

1$ echo $PATH

Набор переменных, которые вы используете постоянно, и называется вашим окружением или средой (environment). Чтобы просмотреть весь набор ваших переменных окружения, введите из командной строки следующую команду:

1$ env

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

Командные сценарии

Я лентяй. Помните команду find, предназначенную для вывода всех файлов, содержащих упоминание стандартов ISO, приведенную чуть ранее в данном рецепте? И кто же захочет каждый раз вводить эту команду вручную? Может быть, кто-то и захочет, но уж точно не я. Да и вам это тоже делать не обязательно. Гораздо проще создать небольшой текстовый файл, выглядящий так, как показано в листинге.

1234// Файл командного сценария, позволяющий избежать ручного// ввода длинной команды find#!/bin/bashfind . -type f -exec grep $1 {} ; -print

Сохраните этот файл под именем, например, findfile, а затем введите следующую команду, назначение которой заключается в том, чтобы сообщить оболочке, что исполнение команд, содержащихся в этом файле, разрешено:

1$ chmod +x findfile

Теперь вы сможете выполнить следующую команду:

1$ ./findfile «ISO [0-9]+»

В ответ на эту команду оболочка откроет ваш файл, но перед этим установит переменную с именем $1 на значение, равное ISO [0-9]+. Первая строка файла сообщает оболочке использовать bash для интерпретации всех остальных содержащихся в нем команд. По умолчанию будет использоваться оригинальная оболочка Bourne, так что этот сценарий будет работать и так, но оболочка Bourne Again, bash, намного лучше. Вторая строка сценария содержи команду find, которая и будет исполняться, причем вместо переменной $1 будет подставлено ее значение — ISO [0-9]+.

Каков смысл комбинации точки и символа наклонной косой черты (./) в начале команды? Эта комбинация связана с переменной $PATH, которая представляет собой список каталогов, разделенных символом точки с запятой, в которых оболочка будет искать вводимые вами команды. Если команда, которую вы вводите, не содержит явного указания на путь к каталогу, в котором она находится, то оболочка выполнит поиск по каталогам, перечисленным в переменной $PATH. Поскольку комбинация ./ выглядит как путь, то оболочка пропускает поиск по каталогам, указанным в переменной $PATH, и исполняет файл сразу же. Как вы уже знаете, в мире UNIX точкой обозначается текущий каталог.

По умолчанию, текущий каталог не указывается в списке каталогов, определенном в переменной $PATH. Делается это по соображениям безопасности. Если у вас есть команда, которую вам требуется выполнять регулярно, вы можете поместить ее в каталог /usr/local/bin или создать каталог bin в вашем домашнем каталоге. Я поступил именно так и сообщил оболочке о том, что мои команды следует искать именно там, добавив в мой файл конфигурирования запуска .bash_profile следующую строку:

1export PATH=~/bin:$PATH

После того как я поместил мой файл findfile в этот каталог, я в любой момент могу запустить сценарий, введя из командной строки следующее:

1$ findfile «ISO [0-9]+»

Сложные сценарии с циклами и условными операторами

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

Допустим, что нам требуется переименовать все файлы, созданные приложением Word и расположенные в конкретном каталоге (скажем, добавив суффикс -old к имени каждого из файлов). Сделать это можно с помощью следующей команды:

1$ for file in *.doc; do mv $file $file-old; done

Фактически эта команда представляет собой цикл, в котором создается список всех файлов с расширением .doc (фактически, это равносильно команде ls *.doc), а затем имена этих файлов меняются командой mv. В конец имени каждого файла добавляется суффикс –old. После исполнения этой команды все файлы, заканчивающиеся на .doc, будут оканчиваться на .doc-old. Существует и способ поместить суффикс –old перед расширением имени файла (таким образом, все файлы Word в этом каталоге будут выглядеть так: *-old.doc), но для этого следует дать команду man bash и более пристально изучить man-страницу оболочки bash. Впрочем, я редко использую такие командные строки, поскольку я всегда предпочитаю сохранять командные строки в файлах.

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

1234567// Командный сценарий для автоматического редактирования// большого количества файлов формата XML#!/bin/bashfor file in *.xml; domv $file $file.bak;sed -e s/OldName/NewName/g $file.bak >$file;done

Командный сценарий, приведенный в листинге 4.6, циклически обрабатывает все файлы, применяя к каждому из них команду sed. Команда sed — это еще одна исключительно полезная программа UNIX, с которой вам необходимо ознакомиться. Sed представляет собой неинтерактивный текстовый редактор, работающий из командной строки. В данном примере он применяется для выполнения простой программы замены, которая автоматически заменяет все вхождения строки oldName на строку NewName.

Sed (Stream EDitor) — это потоковый текстовый редактор, одновременно являющийся и языком программирования. Он применяет различные предопределенные текстовые преобразования к последовательному потоку текстовых данных (обычно файлу), редактирует каждую строку в соответствии с правилами, заданными в sed-скрипте, и выводит результат в выводной поток. Sed является настолько мощным средством, что заслуживает пристального изучения. Помимо man-страницы sed, рекомендуется посетить следующие страницы Википедии: http://en.wikipedia.org/wiki/Sed (англ.) и http://ru.wikipedia.org/wiki/Sed (рус.) и пройтись по приведенным там ссылкам.

Sed не изменяет исходного файла, поэтому я изменил имя исходного файла, добавив в его конец суффикс .bak, а затем перенаправил вывод от sed в файл с исходным именем. Таким образом я сэкономил целый день работы, которая в данном случае заняла что-то около трех минут. Пример, показывающий, как я добился поставленной цели, приведен в листинге.

123456789// Командный сценарий, примененный для пакетного изменения// множества файлов#!/bin/bashfor file in *.xml; doif (grep –q OldName $file); thenmv $file $file.bak;sed -e s/OldName/NewName/g $file.bak >$file;fi;done

Сценарий, приведенный в листинге выше, обрабатывает только файлы, в которых команда grep обнаружила строку OldName. Благодаря этому, редактированию подверглись только те файлы, которые в этом нуждались.

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

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

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

Mac OS X предоставляет целых четыре различных способа достижения этой цели — два старомодных, принятых в те годы, когда UNIX была еще очень молодой операционной системой, и два современных, более характерных для Mac.

Старомодные подходы

Если вы хотите, чтобы ваша программа автоматически запустилась в заданный момент времени в будущем и отработала только один раз, воспользуйтесь командой at. Создайте скрипт и назовите его, например, test.sh. Не называйте его просто test, поскольку это — встроенное имя, используемое оболочкой, и вы будете долго ломать голову, пытаясь понять, почему скрипт не работает. Если вы не можете придумать ничего творческого, воспользуйтесь примером, приведенным в листинге.

1234// Простой скрипт, используемый в качестве // примера для запуска по расписанию#!/bin/bashecho «hello» >>trash.txt

Не забудьте запустить команду chmod +x, указав в качестве аргумента имя нового файла:

1$ chmod +x test.sh

Когда вы запускаете эту новую программу, она добавит строку hеllo в состав файла trash.txt. Теперь попробуйте запустить этот сценарий, дав, например, следующую команду:

1$ at –f test.sh 1735

После этого дождитесь, когда наступит 17:35, и посмотрите на файл trash.txt.

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

123// Редактирование файла crontab с помощью редактора nano$ export EDITOR=/usr/bin/nano$ crontab -e

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

Обратите внимание, что символ ^ означает нажатие клавиши Control, таким образом, обозначение ^G соответствует нажатию клавиатурной комбинации Ctrl+G.

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

12345// Пример файла crontab15 * * * * $HOME/test.sh30 12 * * * $HOME/test.sh35 12 1 * * $HOME/test.sh40 12 * * 1-5 $HOME/test.sh

Первая строка указывает команде cron, что программа, запускаемая этой строкой, должна стартовать через 15 минут после наступления каждого часа. Вторая строка сообщает команде cron о том, что запускаемая этой строкой программа должна запускаться ежедневно в 12:30. Третья строка сообщает, что запускаемая программа должна исполняться в 12:35 в первый день каждого месяца, а четвертая — что программа должна запускаться в 12:40 каждый рабочий день. Чтобы завершить работу редактора nano, нажмите клавиатурную комбинацию Ctrl+X и дайте утвердительный ответ на запрос о том, хотите ли вы сохранить внесенные изменения. Каждый раз, когда задание исполняется, в выводной файл будет добавлена одна строка, так что у вас есть возможность наблюдать за процессом.

Чтобы узнать больше о формате файла crontab, из командной строки дайте следующую команду:

1$ man 5 crontab

Из выведенного руководства вы сможете узнать больше о формате файлов crontab.

Новые методы автоматического запуска сценариев

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

Как быть, если у вас имеется некоторое задание и вы хотите, чтобы это задание было выполнено в конкретный момент времени (при условии, что компьютер включен). При этом, если компьютер на указанный момент окажется выключенным, вам требуется, чтобы задание было исполнено сразу же после того, как компьютер будет включен (первое, что приходит на ум при упоминании таких заданий — это резервное копирование). Существуют два способа выполнения этой задачи.

Во-первых, вы можете поместить ваш сценарий в один из подкаталогов каталога /etc/periodic. Таких вложенных каталогов всего три — daily, weekly и monthly.

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

Сценарии, помещенные в эти каталоги, будут запускаться автоматически через интервалы, о которых несложно догадаться. Говоря точнее, ежедневные задания будут выполняться в 3:15 каждый день, еженедельные — в 3:15 по субботам, а ежемесячные — в 5:30 ежемесячно. Скрипты в каждом каталоге запускаются в алфавитном порядке, поэтому если вы желаете, чтобы один из скриптов всегда выполнялся ранее другого, необходимо позаботиться о соответствующей схеме их именования. Если вы считаете, что такое расписание слишком неудобно и содержит слишком много ограничений, попробуйте пользоваться командой launchd.

Эта программа представляет собой более современный способ запуска заданий с заданными интервалами периодичности, но его описание выходит далеко за рамки рецептов, даваемых в данной статье. Чтобы узнать больше об использовании launchd, прочтите соответствующие man-страницы, введя команды $ man launchd и $ man launchd.plist. Примеры содержимого списков свойств launchd можно найти в каталоге /System/Library/LaunchDaemons.