Перейти к содержанию

14.5 Подстановка команд и функции оболочки (Command Substitution and Shell Functions)

Завершим эту главу обсуждением следующих возможностей оболочки bash:

  • Использование подстановки команд
  • Использование функций оболочки

Использование подстановки команд (Using Command Substitution)

Оболочка bash позволяет использовать подстановку команд (command substitution): можно выполнить команду и вставить её вывод обратно в командную строку в качестве аргумента для другой команды. По существу, подстановка команд позволяет выполнять несколько задач одновременно.

Подстановка команд работает следующим образом: сначала создаётся дочерний процесс, выполняющий первую команду. Стандартный вывод этой команды передаётся обратно в оболочку bash. Оболочка разбивает вывод первой команды на слова, разделённые пробельными символами. После закрытия канала от первой команды (что означает завершение её выполнения) оболочка запускает другой дочерний процесс для выполнения второй команды, используя стандартный вывод первой команды в качестве аргументов.

Например, предположим, что необходимо с помощью команды tail просмотреть последние несколько строк всех файлов в каталоге /etc, содержащих текст «192.168». Для этого можно использовать следующую подстановку команд:

tail $(fgrep -l 192.168 /etc/*)

Сначала выполняется команда fgrep -l для поиска во всех файлах каталога /etc текстовой строки «192.168». Параметр -l, использованный с fgrep, заставляет команду возвращать только список имён файлов, без самого совпавшего текста. Этот список файлов затем передаётся команде tail, которая отображает последние несколько строк каждого полученного файла.

Для решения аналогичной задачи в оболочке bash можно использовать команду xargs. Например, подстановка команд может завершиться неудачей, если первая команда передаёт второй слишком много результатов. В такой ситуации возникает ошибка «слишком много аргументов»:

Argument list too long

В связи с ограничением, установленным ядром Linux, максимальная длина командной строки bash составляет 128 КБ. В этом случае вместо подстановки команд можно использовать xargs. Команда xargs разбивает длинную командную строку на фрагменты по 128 КБ и передаёт каждый фрагмент в качестве аргумента команде, указанной в командной строке xargs. Например, предположим, что необходимо удалить все резервные файлы (которые обычно заканчиваются символом ~) из домашнего каталога. Для этого можно использовать следующую команду:

find ~/ -name "*~" | xargs rm

Команда find сначала формирует список совпадающих файлов и передаёт его на вход команды xargs, которая обрабатывает текстовый поток порциями по 128 КБ и передаёт его на стандартный ввод команды rm.

Завершим эту главу, рассмотрев использование функций оболочки.


Использование функций оболочки (Using Shell Functions)

Оболочка bash позволяет определять функции (functions), которые можно использовать как из командной строки, так и внутри сценария. Определяя функцию, вы по сути создаёте новую «команду», которую можно вызывать и использовать как обычную внутреннюю или внешнюю команду. Функция содержит список команд для выполнения, подобно рассмотренным ранее сценариям. Однако функция не сохраняется в файле — она существует только в памяти. Это означает, что она исчезнет при закрытии текущего сеанса оболочки. Конечно, определение функции можно добавить в один из файлов конфигурации bash, чтобы сделать его постоянным. Синтаксис функции выглядит следующим образом:

function_name()
{
  command1
  command2
  command3
}

Рассмотрим простой пример функции в командной строке. Эта функция не делает особо ничего сложного — она просто отображает содержимое указанного в качестве аргумента каталога:

rtracy@openSUSE:~> show ()
> {
> ls -l $1
> }
rtracy@openSUSE:~> show /tmp
total 84
drwx------ 2 rtracy users 4096 2011-04-30 04:00 keyring-eiVJ50
drwx------ 2 rtracy users 4096 2011-06-24 11:40 keyring-rGEjnD
drwx------ 2 gdm    gdm    4096 2011-06-24 11:40 orbit-gdm

Обратите внимание, что первый аргумент, указанный в командной строке при вызове функции show, был передан в качестве переменной $1 команде ls -l. Если бы при вызове функции было указано несколько аргументов, они были бы переданы в переменные с соответствующими номерами ($1, $2, $3 и так далее).

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

Например, можно объявить в начале сценария функцию, проверяющую существование файла:

function isitthere ()
{
   if [ -s $1 ]
     then
        echo $1 "file exists!"
     else
       echo $1 "file does not exist!"
  fi
}

Затем функцию isitthere можно вызывать в любом месте остальной части сценария. Например, с помощью оператора read можно запросить у пользователя имя файла и записать ввод в переменную MYFILE. Затем это значение передаётся в функцию с помощью следующего оператора:

isitthere $MYFILE

Функция прочитает значение переменной MYFILE, передаст его в $1, а затем воспользуется конструкцией if/then/else, чтобы проверить существование файла.

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

Например, можно взять команды, определённые ранее для функции isitthere, и поместить их в отдельный файл сценария с именем isitthere.sh:

#!/bin/bash
isitthere ()
{
   if [ -s $1 ]
     then
        echo $1 "file exists!"
     else
       echo $1 "file does not exist!"
  fi
}

Теперь функцию isitthere() из файла isitthere.sh можно вызывать и использовать в других сценариях с помощью команды source. Синтаксис: source имя_файла аргументы. Как и прежде, с помощью оператора read в сценарии можно запросить у пользователя имя файла и записать ввод в переменную MYFILE. Затем можно загрузить функцию isitthere() из файла isitthere.sh и передать значение переменной в функцию. Это делается с помощью следующего оператора в сценарии:

source isitthere.sh $MYFILE

Разумеется, этот пример предполагает, что файл isitthere.sh находится в каталоге, входящем в переменную окружения PATH. Если это не так, в операторе source необходимо указать полный путь к файлу.

Теперь вы опытный пользователь оболочки bash! Подведём итоги того, что было изучено в этой главе.

Совет к экзамену

В сценарии можно также включать переменные окружения. Например, строка MAILTO=root в сценарии позволит отправлять уведомления о событиях или другие условные события на учётную запись суперпользователя root. Можно также определить собственную переменную окружения PATH, добавив в сценарий PATH= с последующим списком путей.