Bash (как и сценарии на языке командной оболочки в целом) существует уже очень давно, и каждый день новые люди знакомятся с ее возможностями и приемами автоматизации операций с ее применением. И сейчас, когда компания Microsoft выпустила интерактивную оболочку bash и подсистему команд Unix в Windows 10, самое время узнать, насколько простыми и эффективными могут быть сценарии командной оболочки.
С первых дней существования компьютеров сценарии командной оболочки помогали системным администраторам и программистам выполнять рутинную работу, на которую иначе пришлось бы потратить массу времени. Так что же такое «сценарии командной оболочки» и почему они должны волновать вас? Сценарии — это текстовые файлы с набором команд, следующих в порядке их выполнения, на языке конкретной командной оболочки (в нашем случае bash). Командная оболочка (shell) — это интерфейс командной строки к библиотеке команд в операционной системе.
Сценарии командной оболочки по своей сути являются крохотными программами, написанными с использованием команд операционной системы для автоматизации специальных задач — часто таких, выполнение которых вручную не доставляет никакого удовольствия, например, для сбора информации из сети, слежения за использованием дискового пространства, загрузки данных о погоде, переименования файлов и многих других. В виде сценария нетрудно даже реализовать простенькие игры! Такие сценарии могут включать несложную логику, например, инструкции if, которые вы встречали в других языках, но могут быть еще проще, как вы увидите далее.
Многие разновидности командных оболочек, такие как tcsh, zsh и даже популярная оболочка bash, доступны в операционных системах OS X, BSD и Linux. В этой книге основное внимание уделяется главной опоре Unix — командной оболочке bash. Каждая оболочка имеет свои особенности и возможности, но большинство пользователей Unix в первую очередь обычно знакомятся именно с bash. В OS X программа Terminal открывает окно с оболочкой bash (рис. 0.1). В Linux имеется большое разнообразие программ с командной оболочкой, но чаще всего встречаются консоли командной строки: gnome-terminal для GNOME и konsole для KDE. Эти приложения можно настраивать на использование разных типов командных оболочек, но все они по умолчанию используют bash. Фактически в любой Unix-подобной системе, открыв программу-терминал, вы по умолчанию получите доступ к командной оболочке bash.
Рис. 0.1. Вывод версии bash в окне приложения Terminal в OS X
Использование терминала для взаимодействия с операционной системой может показаться сложнейшей задачей. Однако со временем намного естественней становится просто открыть терминал, чтобы быстро изменить что-то в системе, чем перебирать мышью пункты меню, пытаясь отыскать параметры для изменения.
ПРИМЕЧАНИЕ
В августе 2016 года компания Microsoft выпустила версию bash для Windows 10 Anniversary. То есть теперь ее могут запускать пользователи Windows. В приложении A приводятся инструкции по установке bash для Windows 10, но вообще эта книга предполагает, что вы работаете в Unix-подобной системе, такой как OS X или Linux. Вы можете опробовать предлагаемые сценарии в Windows 10, но мы не даем никаких гарантий и сами не тестировали их таким образом! Тем не менее оболочка bash славится своей переносимостью и многие сценарии из этой книги должны работать и в Windows.
Главная особенность bash — возможность запускать команды в системе. Давайте опробуем короткий пример «Hello World». Команда echo оболочки bash выводит текст на экран, например:
$ echo "Hello World"
Введите данный текст в командной строке bash, и вы увидите, как на экране появятся слова Hello World. Эта строка кода запускает команду echo, хранящуюся в стандартной библиотеке bash. Список каталогов, в которых bash будет искать стандартные команды, хранится в переменной окружения с именем PATH. Вы можете запустить команду echo с переменной PATH, чтобы увидеть ее содержимое, как показано в листинге 0.1.
Листинг 0.1. Вывод текущего содержимого переменной окружения PATH
$ echo $PATH
/Users/bperry/.rvm/gems/ruby-2.1.5/bin:/Users/bperry/.rvm/gems/ruby-2.1.5@global/bin:/Users/bperry/.rvm/rubies/ruby-2.1.5/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/MacGPG2/bin:/Users/bperry/.rvm/bin
ПРИМЕЧАНИЕ
В листингах, где присутствуют вводимые команды и результаты их выполнения, вводимые команды выделены жирным и начинаются с символа $, чтобы вы могли отличить их от вывода, полученного в ходе выполнения команды.
Каталоги в этом выводе отделяются друг от друга двоеточием. Именно их проверит оболочка bash, когда от нее потребуют запустить программу или команду. Если искомая команда хранится в каком-то другом каталоге, bash не сможет запустить ее. Обратите также внимание, что bash проверит перечисленные каталоги именно в том порядке, в каком они перечислены в переменной PATH. Это важно, если у вас имеется две команды с одинаковыми именами, но хранящиеся в разных каталогах, включенных в PATH. Если обнаружится проблема с поиском некоторой команды, попробуйте выполнить команду which с ее именем, как показано в листинге 0.2, чтобы увидеть, в каком каталоге из PATH ее найдет оболочка.
Листинг 0.2. Поиск команд в PATH с помощью which
$ which ruby
/Users/bperry/.rvm/rubies/ruby-2.1.5/bin/ruby
$ which echo
/bin/echo
Теперь, вооруженные этой информацией, вы сможете переместить или скопировать файл в один из каталогов, перечисленных командой echo $PATH, как, например, в листинге 0.1, и затем команда начнет запускаться. Мы будем использовать which на протяжении всей книги для определения полного пути к командам. Это удобный инструмент для отладки содержимого переменной PATH.
На всем протяжении книги нам предстоит писать сценарии, которые потом будем использовать в других сценариях, поэтому для нас важна простота вызова новых сценариев. Вы можете настроить переменную PATH так, чтобы ваши собственные сценарии вызывались автоматически, как любые другие команды, в момент запуска новой командной оболочки. Когда открывается новый сеанс командной оболочки, она первым делом читает сценарий входа в домашнем каталоге (/Users/<username> в OS X или /home/<username> в Linux) и выполняет любые команды, перечисленные в нем. Сценарий входа называется .login, .profile, .bashrc или .bash_profile, в зависимости от системы. Чтобы узнать, какой из этих файлов используется как сценарий входа, добавьте в каждый из них следующую строку, заменив последнее слово соответствующим именем файла:
echo this is .profile
Затем выполните вход. Вверху окна терминала должна появиться строка, сообщающая имя файла сценария, выполненного при входе. Если вы откроете терминал и увидите this is .profile, значит, ваша оболочка загружает файл .profile; если вы увидите this is .bashrc, значит, загружается файл .bashrc; и так далее. Однако описанное поведение зависит от типа командной оболочки.
Вы можете добавить в сценарий входа настройку переменной PATH, включив в нее другие каталоги. Здесь же можно подкорректировать любые другие настройки bash, такие как внешний вид строки приглашения к вводу, содержимое переменной PATH и любые другие параметры. Например, воспользуемся командой cat, чтобы заглянуть в измененный сценарий входа .bashrc. Команда cat принимает аргумент с именем файла и выводит его содержимое в окно консоли, как показано в листинге 0.3.
Листинг 0.3. Измененный файл .bashrc, включающий в переменную PATH каталог RVM
$ cat ~/.bashrc
export PATH="$PATH:$HOME/.rvm/bin" # Добавить в PATH каталог RVM для работы
Команда вывела содержимое файла .bashrc, в котором переменной PATH присваивается новое значение, позволяющее локальной версии RVM (Ruby Version Manager — диспетчер версий Ruby) управлять любыми установленными версиями Ruby. Так как сценарий .bashrc настраивает PATH каждый раз, когда открывается новый сеанс работы с командной оболочкой, диспетчер RVM будет доступен по умолчанию.
Аналогично можно открыть доступ к своей библиотеке сценариев командной оболочке. Для этого в своем домашнем каталоге создайте папку, куда будут помещаться разрабатываемые сценарии. Затем добавьте ее в переменную PATH в сценарии входа, чтобы упростить вызов сценариев из нее.
Чтобы выяснить путь к домашнему каталогу, дайте команду echo $HOME, которая выведет в окне терминала полный путь. Перейдите в указанный каталог и создайте папку для разрабатываемых сценариев (мы рекомендуем назвать ее scripts). Затем добавьте эту папку в свой сценарий входа, для чего откройте файл сценария в текстовом редакторе и добавьте в начало файла следующую строку, заменив /path/to/scripts/ на путь к папке с вашими сценариями:
export PATH="/path/to/scripts/:$PATH"
Затем вы сможете запустить любой сценарий из этой папки как обычную команду.
К настоящему моменту мы уже воспользовались некоторыми командами, такими как echo, which и cat. Но мы использовали их по отдельности, а не вместе, то есть не в составе сценария. Давайте напишем сценарий, который выполнит их все последовательно, как показано в листинге 0.4. Этот сценарий выведет Hello World, затем путь к сценарию neqn, который по умолчанию должен быть доступен в оболочке bash. Затем использует этот путь для вывода содержимого сценария neqn на экран. (На данный момент содержимое neqn для нас не важно; мы просто выбрали первый попавшийся сценарий для примера.) Этот пример наглядно демонстрирует использование сценария для выполнения группы команд по порядку, в данном случае, чтобы увидеть полный путь к сценарию и содержимое сценария.
Листинг 0.4. Содержимое нашего первого сценария командной оболочки
echo "Hello World"
echo $(which neqn)
cat $(which neqn)
Откройте текстовый редактор (в Linux, например, большой популярностью пользуются редакторы Vim и gedit, а в OS X — TextEdit) и введите содержимое листинга 0.4. Затем сохраните сценарий с именем intro в своем каталоге для разрабатываемых сценариев. Сценарии командной оболочки не требуют специального расширения файлов, так что сохраните файл с именем без расширения (или, если пожелаете, добавьте расширение .sh, но в этом нет необходимости). Первая строка в сценарии вызывает команду echo, чтобы просто вывести текст Hello World. Вторая строка чуть сложнее; она использует команду which для поиска файла сценария neqn и затем с помощью echo выводит найденный путь на экран. Чтобы выполнить такую связку команд, где одна передается другой в виде аргумента, bash использует подоболочку, в которой выполняет вторую команду и сохраняет ее вывод для передачи первой. В нашем примере подоболочка выполнит команду which, которая вернет полный путь к сценарию neqn. Затем этот путь будет передан как аргумент команде echo, которая просто выведет его на экран. Наконец, тот же трюк с подоболочкой используется для передачи пути к сценарию neqn команде cat, которая выведет содержимое сценария neqn.
Сохраните файл и запустите сценарий в окне терминала. Вы должны увидеть результат, показанный в листинге 0.5.
Листинг 0.5. Результат запуска нашего первого сценария командной оболочки
$ sh intro
Hello World
/usr/bin/neqn
#!/bin/sh
# Присутствие этого сценария не должно расцениваться как наличие поддержки
# GNU eqn и groff -Tascii|-Tlatin1|-Tutf8|-Tcp1047
GROFF_RUNTIME="${GROFF_BIN_PATH=/usr/bin}:"
PATH="$GROFF_RUNTIME$PATH"
export PATH
exec eqn -Tascii ${1+"$@"}
# eof
$
Запуск сценария производится с помощью команды sh, которой имя сценария intro передается как аргумент. Команда sh обойдет все строки в файле и выполнит их, как если бы это были команды bash, введенные в окне терминала. Как показано в листинге 0.5, сначала на экран выводится строка Hello World , затем путь к файлу neqn . В заключение выводится содержимое файла neqn ; это исходный код короткого сценария командной оболочки neqn, хранящегося на вашем жестком диске (в OS X, по крайней мере, в Linux содержимое этого сценария может немного отличаться).
Для запуска сценариев не обязательно использовать команду sh. Если добавить еще одну строку в сценарий intro и изменить его разрешения в файловой системе, его можно будет запускать непосредственно, без команды sh, как любые другие команды. Откройте сценарий intro в текстовом редакторе и измените его, как показано ниже:
#!/bin/bash
echo "Hello World"
echo $(which neqn)
cat $(which neqn)
Мы добавили единственную строку в самое начало файла, ссылающуюся на путь в файловой системе /bin/bash . Эта строка называется shebang. С ее помощью командная оболочка определяет, какую программу запустить для интерпретации сценария. Здесь в качестве интерпретатора мы указали bash. Вы можете встретить другие строки shebang, например, в сценариях на языке Perl (#!/usr/bin/perl) или Ruby (#!/usr/bin/env ruby).
После добавления строки нам еще необходимо установить права доступа к файлу, разрешающие выполнять его как обычную программу. Для этого в окне терминала выполните команды, показанные в листинге 0.6.
Листинг 0.6. Изменение прав доступа к файлу сценария intro, разрешающих его выполнение
$ chmod +x intro
$ ./intro
Hello World
/usr/bin/neqn
#!/bin/sh
# Присутствие этого сценария не должно расцениваться как наличие поддержки
# GNU eqn и groff -Tascii|-Tlatin1|-Tutf8|-Tcp1047
GROFF_RUNTIME="${GROFF_BIN_PATH=/usr/bin}:"
PATH="$GROFF_RUNTIME$PATH"
export PATH
exec eqn -Tascii ${1+"$@"}
# eof
$
Для изменения прав доступа мы использовали команду chmod и передали ей аргумент +x, который требует от команды дать указанному файлу право на выполнение, и имя самого файла. После настройки права на выполнение для сценария, чтобы запускать его как обычную программу, мы можем вызвать сценарий непосредственно, как показано в строке , без вызова самой оболочки bash. Это общепринятая практика в разработке сценариев командной оболочки, и вы со временем поймете ее полезность. Большинству сценариев, которые мы напишем в этой книге, так же потребуется дать право на выполнение, подобно сценарию intro.
Мы привели лишь простой пример, чтобы показать, как запускать сценарии командной оболочки и как использовать сценарии для запуска других сценариев. Во многих сценариях в этой книге мы задействуем именно такой метод, и вы еще не раз увидите строки shebang в будущем.
Кого-то из вас может беспокоить вопрос: почему для создания сценариев предпочтительнее использовать язык командной оболочки bash вместо более новых и мощных языков, таких как Ruby и Go. Да, эти языки гарантируют переносимость между разными типами систем, но они не устанавливаются по умолчанию. Причина проста: на любой машине с операционной системой Unix имеется командная оболочка, и на подавляющем большинстве из них используется оболочка bash. Как отмечалось в начале главы, компания Microsoft недавно выпустила для Windows 10 ту же самую командную оболочку bash, которая имеется во всех основных дистрибутивах Linux и OS X. То есть теперь сценарии командной оболочки стали еще более переносимыми с минимумом усилий с вашей стороны. Кроме того, сценарии на языке командной оболочки позволяют быстрее и проще решать задачи обслуживания и администрирования системы, чем сценарии на других языках. Оболочка bash все еще далека от идеала, но в этой книге вы узнаете, как смягчить некоторые ее недостатки.
В листинге 0.7 приводится пример маленького, удобного и полностью переносимого сценария командной оболочки (фактически, это однострочная команда на bash!). Сценарий определяет общее количество страниц во всех документах OpenOffice, находящихся в указанной папке, и может пригодиться писателям.
Листинг 0.7. Сценарий для определения общего количества страниц во всех документах OpenOffice в указанной папке
#!/bin/bash
echo "$(exiftool *.odt | grep Page-count | cut -d ":" -f2 | tr '\n' '+')""0" | bc
Не будем обсуждать тонкости работы этого сценария — в конце концов, мы только в самом начале пути. Но в общих чертах отметим, что он извлекает информацию о количестве страниц из каждого документа, выстраивает строку из полученных чисел, перемежая их операторами сложения, и передает ее калькулятору командной строки для вычисления суммы. На все про все оказалось достаточно одной строки кода. В книге вы найдете еще множество таких же потрясающих сценариев, как этот, и после некоторой практики он покажется вам невероятно простым!
Теперь вы должны представлять, как создаются сценарии командной оболочки, если прежде вы этим не занимались. Создание коротких сценариев для решения специализированных задач заложено в основу философии Unix. Умение писать собственные сценарии и расширять возможности системы Unix под свои потребности даст вам огромную власть. Эта глава лишь намекнула, что ждет вас впереди: множество по-настоящему потрясающих сценариев командной оболочки!
Произносится как «ше-банг». — Примеч. пер.