Книга: Искусство программирования на языке сценариев командной оболочки

Пример 22-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


Команда 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.

Упражнение: Используя только что полученные знания, добавьте в предыдущий пример, преобразования чисел в римскую форму записи, возможность обрабатывать большие числа.

Перенаправление

Перенаправление ввода для функций

Функции -- суть есть блок кода, а это означает, что устройство stdin для функций может быть переопределено (перенаправление stdin) (как в Пример 3-1).

Пример 22-7. Настоящее имя пользователя

#!/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.

Оглавление книги


Генерация: 0.065. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз