Нейл Мэтью - Основы программирования в Linux
Давайте начнем с действительно простой функции.
#!/bin/sh
foo() {
echo "Function foo is executing"
}
echo "script starting"
foo
echo "script ended"
exit 0
Выполняющийся сценарий, выведет на экран следующий текст:
script starting
Function foo is executingscript ended
Как это работает
Данный сценарий начинает выполняться с первой строки. Таким образом, ничего необычного нет, но, когда он находит конструкцию foo() {, он знает, что здесь дается определение функции, названной foo. Он запоминает ссылку на функцию и foo продолжает выполнение после обнаружения скобки }. Когда выполняется строка с единственным именем foo, командная оболочка знает, что нужно выполнить предварительно определенную функцию. Когда функция завершится, выполнение сценария продолжится в строке, следующей за вызовом функции foo.
Вы должны всегда определить функцию прежде, чем сможете ее запустить, немного похоже на стиль, принятый в языке программирования Pascal, когда вызову функции предшествует ее определение, за исключением того, что в командной оболочке нет опережающего объявления (forward) функции. Это ограничение не создает проблем, потому что все сценарии выполняются с первой строки, поэтому если просто поместить все определения функций перед первым вызовом любой функции, все функции окажутся определенными до того, как будут вызваны.
Когда функция вызывается, позиционные параметры сценария $*, [email protected], $#, $1, $2 и т.д. заменяются параметрами функции. Именно так вы считываете параметры, передаваемые функции. Когда функция завершится, они восстановят свои прежние значения.
ПримечаниеНекоторые более ранние командные оболочки не могут восстанавливать значения позиционных параметров после выполнения функций. Не стоит полагаться на описанное в предыдущем абзаце поведение, если вы хотите, чтобы ваши сценарии были переносимыми.
Вы можете заставить функцию возвращать числовые значения с помощью команды return. Обычный способ возврата функцией строковых значений — сохранение строки в переменной, которую можно использовать после завершения функции. Другой способ — вывести строку с помощью команды echo и перехватить результат, как показано далее.
foo() { echo JAY;}
...
result="$(foo)"
Вы можете объявлять локальные переменные в функциях командной оболочки с помощью ключевого слова local. В этом случае переменная действительна только в пределах функции. В других случаях функция может обращаться к переменным командной оболочки, у которых глобальная область действия. Если у локальной переменной то же имя, что и у глобальной, в пределах функции локальная переменная перекрывает глобальную. Для того чтобы убедиться в этом на практике, можно изменить предыдущий сценарий следующим образом.
#!/bin/sh
sample_text="global variable"
foo() {
local sample_text="local variable"
echo "Function foo is executing"
echo $sample_text
}
echo "script starting"
echo $sample_text
foo
echo "script ended"
echo $sample_text
exit 0
При отсутствии команды return, задающей возвращаемое значение, функция возвращает статус завершения последней выполненной команды,
Упражнение 2.12. Возврат значенияВ следующем сценарии, my_name, показано, как в функцию передаются параметры и как функции могут вернуть логический результат true или false. Вы можете вызвать этот сценарий с параметром, задающим имя, которое вы хотите использовать в вопросе.
1. После заголовка командной оболочки определите функцию yes_or_no.
#!/bin/sh
yes_or_no() {
echo "Is your name $* ? "
while true
do
echo -n "Enter yes or no: "
read x
case "$x" in
y | yes ) return 0;;
n | no ) return 1;;
* ) echo "Answer yes or no"
esac
done
}
2. Далее начинается основная часть программы.
echo "Original parameters are $*"
if yes_or_no "$1"
then
echo "Hi $1, nice name"
else
echo "Never mind"
fi
exit 0
Типичный вывод этого сценария может выглядеть следующим образом:
$ ./my_name Rick Neil
Original parameters are Rick Neil
Is your name Rick ?
Enter yes or no: yes
Hi Rick, nice name
$
Как это работает
Когда сценарий начинает выполняться, функция определена, но еще не выполняется. В операторе if сценарий вызывает функцию yes_or_no, передавая ей оставшуюся часть строки как параметры после замены $1 первым параметром исходного сценария строкой Rick. Функция использует эти параметры, в данный момент хранящиеся в позиционных параметрах $1, $2 и т.д., и возвращает значение в вызывающую программу. В зависимости от возвращенного функцией значения конструкция if выполняет один из операторов.
Как видите, у командной оболочки есть большой набор управляющих структур и условных операторов. Вам необходимо познакомиться с некоторыми командами, встроенными в оболочку; после этого вы будете готовы решать реальные программистские задачи без компилятора под рукой!
Команды
В сценариях командной оболочки можно выполнять два сорта команд. Как уже упоминалось, существуют "обычные" команды, которые могут выполняться и из командной строки (называемые внешними командами), и встроенные команды (называемые внутренними командами). Внутренние команды реализованы внутри оболочки и не могут вызываться как внешние программы. Но большинство внутренних команд представлено и в виде автономных программ, это условие — часть требований стандарта POSIX. Обычно, не важно, команда внешняя или внутренняя, за исключением того, что внутренние команды действуют эффективнее.
В этом разделе представлены основные команды, как внутренние, так и внешние, которые мы используем при написании сценариев. Как пользователь ОС Linux, вы, возможно, знаете много других команд, которые принимает командная строка. Всегда помните о том, что вы можете любую из них применить в сценарии в дополнение к встроенным командам, представленным в данном разделе.
breakИспользуйте команду break для выхода из циклов for, while и until до того, как будет удовлетворено управляющее условие. В команде break можно задать дополнительный числовой параметр, указывающий на число циклов, из которых предполагается выход. Однако это может сильно усложнить чтение сценариев, поэтому мы не советуем вам использовать его. По умолчанию break обеспечивает выход из одного цикла.
#!/bin/sh
rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4
for file in fred*
do
if [ -d "$file" ]; then
break;
fi
done
echo first directory starting fred was $file
m -rf fred*
exit 0
Команда :Команда "двоеточие" — фиктивная команда. Она иногда полезна для упрощения логики в условиях, будучи псевдонимом команды true. Поскольку команда : встроенная, она выполняется быстрее, чем true, хотя ее вывод гораздо менее читабелен.
Вы можете найти эту команду в условии для циклов while. Конструкция while : выполняет бесконечный цикл вместо более общего while true.
Конструкция : также полезна для условного задания переменных. Например,
: ${var:=value}
Без : командная оболочка попытается интерпретировать $var как команду.
ПримечаниеВ некоторых более старых версиях сценариев командной оболочки можно встретить двоеточие, применяемое в начале строки для обозначения комментариев, однако в современных сценариях следует всегда применять для обозначения начала комментариев знак #, поскольку этот вариант действует эффективнее.
#!/bin/sh
rm -f fred
if [ -f fred ]; then
:
else
echo file fred did not exist
fi
exit 0
continueКак и одноименный оператор языка С, эта команда заставляет охватывающий ее цикл for, while или until начать новый проход или следующую итерацию. При этом переменная цикла принимает следующее значение в списке.