П О Р Т А Л                            
С Е Т Е В Ы Х                          
П Р О Е К Т О В                        
  
Поиск по сайту:
                                                 
Главная

О проекте

Web-мастеру
     HTML & JavaScript
     SSI
     Perl
     PHP
     XML & XSLT
     Unix Shell

MySQL

Безопасность

Хостинг

Другое








Самое читаемое:

Учебник PHP - "Для Чайника".
Просмотров 3498 раз(а).

Иллюстрированный самоучитель по созданию сайтов.
Просмотров 6101 раз(а).

Учебник HTML.
Просмотров 3265 раз(а).

Руководство по PHP5.
Просмотров 5486 раз(а).

Хостинг через призму DNS.
Просмотров 4123 раз(а).

Подборка текстов стандартных документов.
Просмотров 55763 раз(а).

Учебник PHP - Самоучитель
Просмотров 3078 раз(а).

Документация на MySQL (учебник & справочное руководство)
Просмотров 5441 раз(а).

Внешние атаки...
Просмотров 3823 раз(а).

Учебник PHP.
Просмотров 2819 раз(а).

SSI в примерах.
Просмотров 37454 раз(а).



 
 
| Добавить в избранное | Сделать стартовой | Помощь





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
Important

Команда shift вполне применима и к аргументам функций (см. Пример 33-11).

В отличие от других языков программирования, в сценариях на языке командной оболочке, аргументы в функции передаются по значению. Переменные (которые фактически являются указателями) при передаче в функции в виде параметров, интерпретируются как строковые литералы. Функции всегда интерпретируют свои аргументы буквально.

Механизм косвенных ссылок на переменные (см. Пример 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 [51] может иметь необязательный аргумент типа 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

#  Упражнение:
#  ---------------
#  Сделайте этот сценарий интерактивным,
#+ т.е. заставьте сценарий запрашивать числа для сравнения у пользователя (два числа).
Tip

Для случаев, когда функция должна возвращать строку или массив, используйте специальные переменные.

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.

Important

Наибольшее положительное целое число, которое может вернуть функция -- 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.


Назад | Вперед
Содержание (общее) | Содержание раздела | Содержание подраздела



Если Вы не нашли что искали, то рекомендую воспользоваться поиском по сайту: