Пример 22-2. Функция с аргументами
#!/bin/bash
# Функции и аргументы
DEFAULT=default # Значение аргумента по-умолчанию.
func2 () {
if [ -z "$1" ] # Длина аргумента #1 равна нулю?
then
echo "-Аргумент #1 имеет нулевую длину.-" # Или аргумент не был передан функции.
else
echo "-Аргумент #1: "$1".-"
fi
variable=${1-$DEFAULT} # Что делает
echo "variable = $variable" #+ показанная подстановка параметра?
# ---------------------------
# Она различает отсутствующий аргумент
#+ от "пустого" аргумента.
if [ "$2" ]
then
echo "-Аргумент #2: "$2".-"
fi
return 0
}
echo
echo "Вызов функции без аргументов."
func2
echo
echo "Вызов функции с "пустым" аргументом."
func2 ""
echo
echo "Вызов функции с неинициализированным аргументом."
func2 "$uninitialized_param"
echo
echo "Вызов функции с одним аргументом."
func2 first
echo
echo "Вызов функции с двумя аргументами."
func2 first second
echo
echo "Вызов функции с аргументами "" "second"."
func2 "" second # Первый параметр "пустой"
echo # и второй параметр -- ASCII-строка.
exit 0
Команда shift вполне применима и к аргументам функций (см. Пример 33-10).
В отличие от других языков программирования, в сценариях на языке командной оболочке, в функции передаются аргументы по значению[ 50 ]. Если имена переменных (которые фактически являются указателями) передаются функции в виде аргументов, то они интерпретируются как обычные строки символов и не могут быть разыменованы. Функции интерпретируют свои аргументы буквально.
Exit и Return
код завершения
Функции возвращают значение в виде кода завершения. Код завершения может быть задан явно, с помощью команды return, в противном случае будет возвращен код завершения последней команды в функции (0 -- в случае успеха, иначе -- ненулевой код ошибки). Код завершения в сценарии может быть получен через переменную $?.
return
Завершает исполнение функции. Команда return[ 51 ] может иметь необязательный аргумент типа integer, который возвращается в вызывающий сценарий как "код завершения" функции, это значение так же записывается в переменную $?.
Пример 22-3. Наибольшее из двух чисел
#!/bin/bash
# max.sh: Наибольшее из двух целых чисел.
E_PARAM_ERR=-198 # Если функции передано меньше двух параметров.
EQUAL=-199 # Возвращаемое значение, если числа равны.
max2 () # Возвращает наибольшее из двух чисел.
{ # Внимание: сравниваемые числа должны быть меньше 257.
if [ -z "$2" ]
then
return $E_PARAM_ERR
fi
if [ "$1" -eq "$2" ]
then
return $EQUAL
else
if [ "$1" -gt "$2" ]
then
return $1
else
return $2
fi
fi
}
max2 33 34
return_val=$?
if [ "$return_val" -eq $E_PARAM_ERR ]
then
echo "Функции должно быть передано два аргумента."
elif [ "$return_val" -eq $EQUAL ]
then
echo "Числа равны."
else
echo "Наибольшее из двух чисел: $return_val."
fi
exit 0
# Упражнение:
# ---------------
# Сделайте этот сценарий интерактивным,
#+ т.е. заставьте сценарий запрашивать числа для сравнения у пользователя (два числа).
Для случаев, когда функция должна возвращать строку или массив, используйте специальные переменные.
count_lines_in_etc_passwd()
{
[[ -r /etc/passwd ]] && REPLY=$(echo $(wc -l < /etc/passwd))
# Если файл /etc/passwd доступен на чтение, то в переменную REPLY заносится число строк.
# Возвращаются как количество строк, так и код завершения.
}
if count_lines_in_etc_passwd
then
echo "В файле /etc/passwd найдено $REPLY строк."
else
echo "Невозможно подсчитать число строк в файле /etc/passwd."
fi
# Спасибо S.C.
Пример 22-4. Преобразование чисел в римскую форму записи
#!/bin/bash
# Преобразование чисел из арабской формы записи в римскую
# Диапазон: 0 - 200
# Расширение диапазона представляемых чисел и улучшение сценария
# оставляю вам, в качестве упражнения.
# Порядок использования: roman number-to-convert
LIMIT=200
E_ARG_ERR=65
E_OUT_OF_RANGE=66
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` number-to-convert"
exit $E_ARG_ERR
fi
num=$1
if [ "$num" -gt $LIMIT ]
then
echo "Выход за границы диапазона!"
exit $E_OUT_OF_RANGE
fi
to_roman () # Функция должна быть объявлена до того как она будет вызвана.
{
number=$1
factor=$2
rchar=$3
let "remainder = number - factor"
while [ "$remainder" -ge 0 ]
do
echo -n $rchar
let "number -= factor"
let "remainder = number - factor"
done
return $number
# Упражнение:
# --------
# Объясните -- как работает функция.
# Подсказка: деление последовательным вычитанием.
}
to_roman $num 100 C
num=$?
to_roman $num 90 LXXXX
num=$?
to_roman $num 50 L
num=$?
to_roman $num 40 XL
num=$?
to_roman $num 10 X
num=$?
to_roman $num 9 IX
num=$?
to_roman $num 5 V
num=$?
to_roman $num 4 IV
num=$?
to_roman $num 1 I
echo
exit 0
См. также Пример 10-28.
Наибольшее положительное целое число, которое может вернуть функция -- 255. Команда return очень тесно связана с понятием код завершения, что объясняет это специфическое ограничение. К счастью существуют различные способы преодоления этого ограничения.
Пример 22-5. Проверка возможности возврата функциями больших значений
#!/bin/bash
# return-test.sh
# Наибольшее целое число, которое может вернуть функция, не может превышать 256.
return_test () # Просто возвращает то, что ей передали.
{
return $1
}
return_test 27 # o.k.
echo $? # Возвращено число 27.
return_test 255 # o.k.
echo $? # Возвращено число 255.
return_test 257 # Ошибка!
echo $? # Возвращено число 1.
return_test -151896 # Как бы то ни было, но для больших отрицательных чисел проходит!
echo $? # Возвращено число -151896.
exit 0
Как видно из примера, функции могут возвращать большие отрицательные значения (имеются ввиду -- большие по своему абсолютному значению, прим. перев.). Используя эту особенность, можно обыграть возможность получения от функций большие положительные значения.
Еще один способ -- использовать глобальные переменные для хранения "возвращаемого значения".
Return_Val= # Глобальная переменная, которая хранит значение, возвращаемое функцией.
alt_return_test ()
{
fvar=$1
Return_Val=$fvar
return # Возвратить 0 (успешное завершение).
}
alt_return_test 1
echo $? # 0
echo "Функция вернула число $Return_Val" # 1
alt_return_test 255
echo "Функция вернула число $Return_Val" # 255
alt_return_test 257
echo "Функция вернула число $Return_Val" # 257
alt_return_test 25701
echo "Функция вернула число $Return_Val" #25701
Пример 22-6. Сравнение двух больших целых чисел
#!/bin/bash
# max2.sh: Наибольшее из двух БОЛЬШИХ целых чисел.
# Это модификация предыдущего примера "max.sh",
# которая позволяет выполнять сравнение больших целых чисел.
EQUAL=0 # Если числа равны.
MAXRETVAL=255 # Максимально возможное положительное число, которое может вернуть функция.
E_PARAM_ERR=-99999 # Код ошибки в параметрах.
E_NPARAM_ERR=99999 # "Нормализованный" код ошибки в параметрах.
max2 () # Возвращает наибольшее из двух больших целых чисел.
{
if [ -z "$2" ]
then
return $E_PARAM_ERR
fi
if [ "$1" -eq "$2" ]
then
return $EQUAL
else
if [ "$1" -gt "$2" ]
then
retval=$1
else
retval=$2
fi
fi
# -------------------------------------------------------------- #
# Следующие строки позволяют "обойти" ограничение
if [ "$retval" -gt "$MAXRETVAL" ] # Если больше предельного значения,
then # то
let "retval = (( 0 - $retval ))" # изменение знака числа.
# (( 0 - $VALUE )) изменяет знак числа.
fi
# Функции имеют возможность возвращать большие *отрицательные* числа.
# -------------------------------------------------------------- #
return $retval
}
max2 33001 33997
return_val=$?
# -------------------------------------------------------------------------- #
if [ "$return_val" -lt 0 ] # Если число отрицательное,
then # то
let "return_val = (( 0 - $return_val ))" # опять изменить его знак.
fi # "Абсолютное значение" переменной $return_val.
# -------------------------------------------------------------------------- #
if [ "$return_val" -eq "$E_NPARAM_ERR" ]
then # Признак ошибки в параметрах, при выходе из функции так же поменял знак.
echo "Ошибка: Недостаточно аргументов."
elif [ "$return_val" -eq "$EQUAL" ]
then
echo "Числа равны."
else
echo "Наиболшее число: $return_val."
fi
exit 0
См. также Пример A-8.
Упражнение: Используя только что полученные знания, добавьте в предыдущий пример, преобразования чисел в римскую форму записи, возможность обрабатывать большие числа.