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

Создание собственных команд

Статья включает:

Что такое сценарий?
Доступные командные интерпретаторы
Пример сценария командного интерпретатора Korn
Эффективность
Рекомендации разработчикам сценариев

Что такое сценарий?

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

Доступные командные интерпретаторы

В операционной системе ЗОСРВ «Нейтрино» чаще всего используется командный интерпретатор ksh, который является общедоступной реализацией командного интерпретатора Korn Shell. Команда sh обычно представляет собой символьную ссылку на командный интерпретатор ksh. Более подробные сведения об этом интерпретаторе см. в:

Операционная система ЗОСРВ «Нейтрино» также предоставляет или использует несколько других сред исполнения сценариев.

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

Запуск сценария командного интерпретатора

Сценарий командного интерпретатора можно запустить следующими способами:

Первая строка

Первая строка многих, если не большинства сценариев имеет следующую форму:

#! interpreter [arg]
Например, сценарии Korn shell начинаются с такой строки:
#! /bin/sh
Эта строка начинается с символа #, который указывает на то, что она является комментарием и должна игнорироваться командным интерпретатором, который обрабатывает сценарий. Первая пара символов, #!, не имеет значения для командного интерпретатора, но код загрузчика в модуле procnto распознает их как команду загрузить следующий за ними исполняемый файл, /bin/sh, и передать ему:
  1. путь к данному интерпретатору;
  2. опциональные аргументы, заданные в первой строке сценария;
  3. путь к сценарию;
  4. аргументы этого сценария, заданные в качестве командно-строковых параметров.

Например, сценарий называется my_script и пользователь вызывает его следующей командой:

./my_script my_arg1 my_arg2 ...

В этом случае procnto загружает:

interpreter [arg] ./my_script my_arg1 my_arg2 ...


Note:
  • интерпретатор не может быть иным, нежели заданный #!.
  • ядро игнорирует атрибуты setuid и getuid у сценария; дочерний процесс будет иметь такие же идентификаторы пользователя и группы, что и родительский процесс.

Некоторые интерпретаторы корректируют список получаемых аргументов:

Для примера рассмотрим простые сценарии, печатающие полученные ими аргументы.

Аргументы сценария ksh

Пусть имеется следующий сценарий ksh_script:

#! /bin/sh
echo $0
for arg in "$@" ; do
echo $arg
done

Если вызвать его с помощью команды

./ksh_script one two three

то загрузчик вызовет его как

/bin/sh ./ksh_script one two three

а затем ksh удалит себя из списка аргументов. Вывод будет иметь следующий вид:

./ksh_script
one
two
three

Аргументы сценария gawk

Рассмотрим версию предыдущего сценария для интерпретатора gawk с именем gawk_script, который будет выглядеть следующим образом:

#!/usr/bin/gawk -f
BEGIN {
for (i = 0; i < ARGC; i++)
print ARGV[i]
}

Аргумент -f важен, т.к. он указывает, что бы утилита gawk прочитала сценарий из заданного файла. Без опции -f данный сценарий не будет работать ожидаемым образом.

Если этот сценарий запустить командой

./gawk_script one two three

то загрузчик вызовет его как

/usr/bin/gawk -f ./gawk_script one two three

а затем gawk заменит полное путевое имя на gawk. Вывод будет иметь следующий вид:

gawk
one
two
three

Аргументы сценария perl

Версия рассматриваемого сценария на языке perl будет выглядеть следующим образом:

#! /usr/bin/perl
for ($i = 0; $i <= $#ARGV; $i++) {
print "$ARGV[$i]\n";
}

Если этот сценарий запустить командой

./perl_script one two three

то загрузчик вызовет его как

/usr/bin/perl ./perl_script one two three

а затем perl удаляет себя и имя сценария из списка аргументов. Вывод будет иметь следующий вид:

one
two
three

Пример сценария командного интерпретатора Korn

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

#!/bin/sh
#
# tfind:
# сценарий, который ищет строки в различных файлах и выводит
# эти строки с помощью утилиты less
case $# in
1)
find . -name '*.[ch]' | xargs grep $1 | less
exit 0 # успешное завершение
esac
echo " Введите команду tfind строка_для_поиска, "
echo " где строка_для_поиска — искомая строка "
echo " "
echo " Например, команда tfind console state просматривает все файлы"
echo " в текущем каталоге и его подкаталогах и отображает все "
echo " экземпляры фразы console state. "
exit 1 # неудачное завершение

Как было описано выше, первая строка указывает программу, /bin/sh, для интерпретации сценария.

Несколько следующих строк являются комментариями, которые описывают действия, выполняемые сценарием. Далее следует конструкция:

case $# in
1)
...
esac

Команда case...in является встроенной командой интерпретатора Korn и представляет собой одну из структур ветвления, эквивалентную оператору switch языка C.

Последовательность символов $# является переменной командного интерпретатора. Чтобы обратиться к переменной в командном интерпретаторе, ее имя следует предварить символом $, тогда командный интерпретатор отличит имя переменной от символьной строки. $# – особая переменная командного интерпретатора, которая содержит количество командно-строковых аргументов, переданных сценарию.

Константа 1) является возможным значением переменной ветвления, которое эквивалентно оператору case в языке C. Этот код проверяет, был ли передан командному интерпретатору ровно один параметр.

Строка esac завершает оператор case. В командах case и if для указания конца структуры ветвления используется имя команды, записанное справа налево.

Внутри оператора case имеется конструкция:

find . -name '*.[ch]' | xargs grep $1 | less

Эта строка выполняет несколько действий и включает в себя следующие компоненты:

Эти команды объединены при помощи символа конвейера |. Конвейер – одна из самых мощных возможностей командного интерпретатора; он принимает выходные данные программы, которая указана слева, и передает их в качестве входных данных программе, которая указана справа. Конвейер позволяет строить сложные действия из более простых компонентов. Более подробные сведения см. в Перенаправление ввода и вывода.

Первый компонент конвейера, find . -name '*.[ch]', использует еще одну мощную и распространенную команду. Большинство файловых систем структурировано в виде рекурсивной иерархии каталогов, а утилита find выполняет рекурсивный поиск в этой иерархии. В этом примере утилита find ищет файлы, которые заканчиваются на .c или на .h (т.е. исходные и заголовочные файлы языка C), и распечатывает их имена.

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

Следующий компонент конвейера, xargs grep $1, выполняет два действия:

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

Конструкция case также включает в себя команду:

exit 0 # успешное завершение

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

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

echo " Введите команду tfind строка_для_поиска, "
echo " где строка_для_поиска – искомая строка "
echo " "
echo " Например, команда tfind console state просматривает все файлы"
echo " в текущем каталоге и его подкаталогах и отображает все "
echo " экземпляры фразы console state. "
exit 1 # неудачное завершение

Эффективность

Сценарии обычно менее эффективны, чем специальные программы, написанные на языке С или С++, поскольку сценарии:

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

Рекомендации разработчикам сценариев

Далее приведены некоторые рекомендации, которые следует учитывать при написании сценариев.




Предыдущий раздел: перейти