Мендель Купер - Искусство программирования на языке сценариев командной оболочки
wait # Ожидание завершения работы подоболочек.
diff list123 list456
Перенаправление ввода/вывода в/из подоболочки производится оператором построения конвейера "|", например, ls -al | (command).
Блок команд, заключенный в фигурные скобки не приводит к запуску дочерней подоболочки.
{ command1; command2; command3; ... }
Глава 20. Ограниченный режим командной оболочки
Команды, запрещенные в ограниченном режиме командной оболочки
Запуск сценария или его части в ограниченном режиме, приводит к наложению ограничений на использование некоторых команд. Эта мера предназначена для ограничения привилегий пользователя, запустившего сценарий, и минимизации возможного ущерба системе, который может нанести сценарий.
В ограниченном режиме запрещена команда cd -- смена текщего каталога.
Запрещено изменять переменные окружения $PATH, $SHELL, $BASH_ENV и $ENV.
Заперщен доступ к переменной $SHELLOPTS.
Запрещено перенаправление вывода.
Запрещен вызов утилит, в названии которых присутствует хотя бы один символ "слэш" (/).
Запрещен вызов команды exec для запуска другого процесса.
Запрещен ряд других команд, которые могут использовать сценарий для выполнения непредусмотренных действий.
Запрещен выход из ограниченного режима.
Пример 20-1. Запуск сценария в ограниченном режиме
#!/bin/bash
# Если sha-bang задать в таком виде: "#!/bin/bash -r"
# то это приведет к включению ограниченного режима с момента запуска скрипта.
echo
echo "Смена каталога."
cd /usr/local
echo "Текущий каталог: `pwd`"
echo "Переход в домашний каталог."
cd
echo "Текущий каталог: `pwd`"
echo
# До сих пор сценарий исполнялся в обычном, неограниченном режиме.
set -r
# set --restricted имеет тот же эффект.
echo "==> Переход в ограниченный режим. <=="
echo
echo
echo "Попытка сменить текущий каталог в ограниченном режиме."
cd ..
echo "Текущий каталог остался прежним: `pwd`"
echo
echo
echo "$SHELL = $SHELL"
echo "Попытка смены командного интерпретатора в ограниченном режиме."
SHELL="/bin/ash"
echo
echo "$SHELL= $SHELL"
echo
echo
echo "Попытка перенаправления вывода в ограниченном режиме."
ls -l /usr/bin > bin.files
ls -l bin.files # Попробуем найти файл, который пытались создать.
echo
exit 0
Глава 21. Подстановка процессов
Подстановка процессов -- это аналог подстановки команд. Операция подстановки команд записывает в переменную результат выполнения некоторой команды, например, dir_contents=`ls -al` или xref=$(grep word datafile). Операция подстановки процессов передает вывод одного процесса на ввод другого (другими словами, передает результат выполнения одной команды -- другой).
Шаблон подстановки команды
Внутри круглых скобок
>(command)
<(command)
Таким образом инициируется подстановка процессов. Здесь, для передачи результата работы процесса в круглых скобках, используются файлы /dev/fd/<n>.[ 49 ]
Между круглой скобкой и символом "<" или ">", не должно быть пробелов, в противном случае это вызовет сообщение об ошибке.
bash$ echo >(true)
/dev/fd/63
bash$ echo <(true)
/dev/fd/63
Bash создает канал с двумя файловыми дескрипторами, --fIn и fOut--. stdin команды true присоединяется к fOut (dup2(fOut, 0)), затем Bash передает /dev/fd/fIn в качестве аргумента команде echo. В системах, где отсутствуют файлы /dev/fd/<n>, Bash может использовать временные файлы. (Спасибо S.C.)
cat <(ls -l)
# То же самое, что и ls -l | cat
sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin)
# Список файлов в трех основных каталогах 'bin', отсортированный по именам файлов.
# Обратите внимание: на вход 'sort' поданы три самостоятельные команды.
diff <(command1) <(command2) # Выдаст различия в выводе команд.
tar cf >(bzip2 -c > file.tar.bz2) $directory_name
# Вызовет "tar cf /dev/fd/?? $directory_name" и затем "bzip2 -c > file.tar.bz2".
#
# Из-за особенностей, присущих некоторым системам, связанным с /dev/fd/<n>,
# канал между командами не обязательно должен быть именованным.
#
# Это можно сделать и так.
#
bzip2 -c < pipe > file.tar.bz2&
tar cf pipe $directory_name
rm pipe
# или
exec 3>&1
tar cf /dev/fd/4 $directory_name 4>&1 >&3 3>&- | bzip2 -c > file.tar.bz2 3>&-
exec 3>&-
# Спасибо S.C.
Ниже приводится еще один очень интересный пример использования подстановки процессов.
# Фрагмент сценария из дистрибутива SuSE:
while read des what mask iface; do
# Некоторые команды ...
done < <(route -n)
# Чтобы проверить это, попробуем вставить команду, выполняющую какие либо действия.
while read des what mask iface; do
echo $des $what $mask $iface
done < <(route -n)
# Вывод на экран:
# Kernel IP routing table
# Destination Gateway Genmask Flags Metric Ref Use Iface
# 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
# Как указывает S.C. -- более простой для понимания эквивалент:
route -n |
while read des what mask iface; do # Переменные берут значения с устройства вывода конвейера (канала).
echo $des $what $mask $iface
done # На экран выводится то же самое, что и выше.
# Однако, Ulrich Gayer отметил, что ...
#+ этот вариант запускает цикл while в подоболочке,
#+ и поэтому переменные не видны за пределами цикла, после закрытия канала.
Глава 22. Функции
Подобно "настоящим" языкам программирования, Bash тоже имеет функции, хотя и в несколько ограниченном варианте. Функция -- это подпрограмма, блок кода который реализует набор операций, своего рода "черный ящик", предназначенный для выполнения конкретной задачи. Функции могут использоваться везде, где имеются участки повторяющегося кода.
function function_name { command... }
или
function_name () { command... }
Вторая форма записи ближе к сердцу C-программистам (она же более переносимая).
Как и в языке C, скобка, открывающая тело функции, может помещаться на следующей строке.
function_name () { command... }
Вызов функции осуществляется простым указанием ее имени в тексте сценария.
Пример 22-1. Простая функция
#!/bin/bash
funky ()
{
echo "Это обычная функция."
} # Функция должна быть объявлена раньше, чем ее можно будет использовать.
# Вызов функции.
funky
exit 0
Функция должна быть объявлена раньше, чем ее можно будет использовать. К сожалению, в Bash нет возможности "опережающего объявления" функции, как например в C.
f1
# Эта строка вызовет сообщение об ошибке, поскольку функция "f1" еще не определена.
declare -f f1 # Это не поможет.
f1 # По прежнему -- сообщение об ошибке.
# Однако...
f1 ()
{
echo "Вызов функции "f2" из функции "f1"."
f2
}
f2 ()
{
echo "Функция "f2"."
}
f1 # Функция "f2", фактически, не вызывается выше этой строки,
#+ хотя ссылка на нее встречается выше, до ее объявления.
# Это допускается.
# Спасибо S.C.
Допускается даже создание вложенных функций, хотя пользы от этого немного.
f1 ()
{
f2 () # вложенная
{
echo "Функция "f2", вложенная в "f1"."
}
}
f2 # Вызывает сообщение об ошибке.
# Даже "declare -f f2" не поможет.
echo
f1 # Ничего не происходит, простой вызов "f1", не означает автоматический вызов "f2".
f2 # Теперь все нормально, вызов "f2" не приводит к появлению ошибки,
#+ поскольку функция "f2" была определена в процессе вызова "f1".
# Спасибо S.C.
Объявление функции может размещаться в самых неожиданных местах.
ls -l | foo() { echo "foo"; } # Допустимо, но бесполезно.
if [ "$USER" = bozo ]
then
bozo_greet () # Объявление функции размещено в условном операторе.
{
echo "Привет, Bozo!"
}
fi
bozo_greet # Работает только у пользователя bozo, другие получат сообщение об ошибке.
# Нечто подобное можно использовать с определеной пользой для себя.
NO_EXIT=1 # Will enable function definition below.
[[ $NO_EXIT -eq 1 ]] && exit() { true; } # Определение функции в последовательности "И-список".
# Если $NO_EXIT равна 1, то объявляется "exit ()".
# Тем самым, функция "exit" подменяет встроенную команду "exit".
exit # Вызывается функция "exit ()", а не встроенная команда "exit".
# Спасибо S.C.
22.1. Сложные функции и сложности с функциями
Функции могут принимать входные аргументы и возвращать код завершения.
function_name $arg1 $arg2
Доступ к входным аргументам, в функциях, производится посредством позиционных параметров, т.е. $1, $2 и так далее.