|
Поиск по сайту:
Главная
О проекте
Web-мастеру
HTML & JavaScript
SSI
Perl
PHP
XML & XSLT
Unix Shell
MySQL
Безопасность
Хостинг
Другое
|
|
22.1. Сложные функции и сложности с функциями
Функции могут принимать входные аргументы и возвращать
код завершения.
function_name $arg1 $arg2
Доступ к входным аргументам, в функциях, производится
посредством позиционных
параметров, т.е. $1, $2 и так далее.
Пример 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
В отличие от других языков программирования, в сценариях
на языке командной оболочке, аргументы в функции передаются
по значению. Переменные (которые фактически являются
указателями) при передаче в функции в виде параметров,
интерпретируются как строковые литералы. Функции всегда интерпретируют
свои аргументы буквально.
Механизм косвенных ссылок на
переменные (см. Пример 34-2) слишком
неудобен для передачи аргументов по ссылке.
Пример 22-3. Передача косвенных ссылок в
функцию
#!/bin/bash
# ind-func.sh: Передача косвенных ссылок в функцию.
echo_var ()
{
echo "$1"
}
message=Hello
Hello=Goodbye
echo_var "$message" # Hello
# А теперь передадим функции косвенную ссылку.
echo_var "${!message}" # Goodbye
echo "-------------"
# Что произойдет, если изменить содержимое переменной "Hello"?
Hello="Hello, again!"
echo_var "$message" # Hello
echo_var "${!message}" # Hello, again!
exit 0
Тут же возникает вопрос: "Возможно ли изменить
значение переменной, которая была передана по
ссылке?"
Пример 22-4. Изменение значения переменной,
переданной в функцию по ссылке.
#!/bin/bash
# dereference.sh
# Изменение значения переменной, переданной в функцию по ссылке.
# Автор: Bruce W. Clare.
dereference ()
{
y=\$"$1" # Имя переменной.
echo $y # $Junk
x=`eval "expr \"$y\" "`
echo $1=$x
eval "$1=\"Некий другой текст \"" # Присвоить новое значение.
}
Junk="Некий текст"
echo $Junk "до того как..." # Некий текст до того как...
dereference Junk
echo $Junk "после того как..." # Некий другой текст после того как...
exit 0
Пример 22-5. Еще один пример разыменования
параметров функции, передаваемых по ссылке.
#!/bin/bash
ITERATIONS=3 # Количество вводимых значений.
icount=1
my_read () {
# При вызове my_read varname,
# выводит предыдущее значение в квадратных скобках,
# затем просит ввести новое значение.
local local_var
echo -n "Введите говое значение переменной "
eval 'echo -n "[$'$1'] "' # Прежнее значение.
read local_var
[ -n "$local_var" ] && eval $1=\$local_var
# Последовательность "And-list": если "local_var" не пуста, то ее значение переписывается в "$1".
}
echo
while [ "$icount" -le "$ITERATIONS" ]
do
my_read var
echo "Значение #$icount = $var"
let "icount += 1"
echo
done
# Спасибо Stephane Chazelas за этот поучительный пример.
exit 0
Exit и
Return
- код завершения
-
Функции возвращают значение в виде кода завершения. Код завершения может
быть задан явно, с помощью команды return, в противном случае будет
возвращен код завершения последней команды в функции
(0 -- в случае успеха,
иначе -- ненулевой код ошибки). Код завершения в
сценарии может быть получен через переменную $?.
- return
-
Завершает исполнение функции. Команда return может иметь
необязательный аргумент типа integer,
который возвращается в вызывающий сценарий как
"код
завершения" функции, это значение так же
записывается в переменную $?.
Пример 22-6. Наибольшее из двух
чисел
#!/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 заносится число строк.
# Возвращаются как количество строк, так и код завершения.
# Команда 'echo' может показаться ненужной, но . . .
#+ она предотвращает вывод лишних пробелов.
}
if count_lines_in_etc_passwd
then
echo "В файле /etc/passwd найдено $REPLY строк."
else
echo "Невозможно подсчитать число строк в файле /etc/passwd."
fi
# Спасибо S.C.
|
Пример 22-7. Преобразование чисел в
римскую форму записи
#!/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-8. Проверка возможности
возврата функциями больших
значений
#!/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
Еще более элегантный способ заключается в
передаче возвращаемого значания команде
echo, для
вывода на stdout,
которое затем снимается со стандартного вывода
конструкцией подстановки команд. См.
обсуждение этого приема в
Section 33.7.
Пример 22-9. Сравнение двух
больших целых чисел
#!/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.
Упражнение:
Используя только что полученные знания,
добавьте в предыдущий пример, преобразования чисел в римскую форму
записи, возможность обрабатывать большие
числа.
|
Перенаправление
- Перенаправление ввода для
функций
-
Функции -- суть есть блок
кода, а это означает, что устройство stdin для функций может быть
переопределено (перенаправление stdin) (как в Пример 3-1).
Пример 22-10. Настоящее имя
пользователя
#!/bin/bash
# По имени пользователя получить его "настоящее имя" из /etc/passwd.
ARGCOUNT=1 # Ожидается один аргумент.
E_WRONGARGS=65
file=/etc/passwd
pattern=$1
if [ $# -ne "$ARGCOUNT" ]
then
echo "Порядок использования: `basename $0` USERNAME"
exit $E_WRONGARGS
fi
file_excerpt () # Производит поиск в файле по заданному шаблону, выводит требуемую часть строки.
{
while read line
do
echo "$line" | grep $1 | awk -F":" '{ print $5 }' # Указывет awk использовать ":" как разделитель полей.
done
} <$file # Подменить stdin для функции.
file_excerpt $pattern
# Да, этот сценарий можно уменьшить до
# grep PATTERN /etc/passwd | awk -F":" '{ print $5 }'
# или
# awk -F: '/PATTERN/ {print $5}'
# или
# awk -F: '($1 == "username") { print $5 }'
# Однако, это было бы не так поучительно.
exit 0
Ниже приводится альтернативный, и возможно менее
запутанный, способ перенаправления ввода для функций.
Он заключается в использовании перенаправления ввода
для блока кода, заключенного в фигурные скобки, в
пределах функции.
# Вместо:
Function ()
{
...
} < file
# Попробуйте так:
Function ()
{
{
...
} < file
}
# Похожий вариант,
Function () # Тоже работает.
{
{
echo $*
} | tr a b
}
Function () # Этот вариант не работает.
{
echo $*
} | tr a b # Наличие вложенного блока кода -- обязательное условие.
# Спасибо S.C.
Назад | Вперед
Содержание (общее) | Содержание раздела | Содержание подраздела
Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту:
|
|
|