Как вернуть значение из bash функции?
Как известно, в bash все функции и команды возвращают лишь код возврата
(ноль - успешное завершение, не-ноль - ошибка).
Для того, чтобы вернуть нечто отличное от числа,
нужно использовать один из следующих приёмов:
- установка глобальной переменной
- использование подстановки (чтение вывода команды)
- передача косвенной ссылки в функцию
Рассмотрим подробнее, как же использовать данные подходы.
Установка глобальной переменной
Что может быть проще, чем загадить глобальную область видимости в функции, и затем считать из неё данные в основной программе?
  function makedirty()
  {
      myresult='oh, sic...!'
  }
  makedirty
  echo $myresult  # oh, sic...!
Оно и понятно, ведь все переменные в bash по-умолчанию - глобальные.
  + Элементарно реализуется
  + Возможность вернуть несколько значений
  - В больших программах может привести к трудноуловимым ошибкам
  -   т.к. кто-то случайно где-то может затереть Вашу переменную
Иcпользование подстановки, aka $(function)
Довольно распространённый механизм. Вы уже, наверняка, сами не зная того, неоднократно им пользовались. Идея в том, что мы сохраняем в переменную всё, что выводит некая команда (функция -- тоже команда). Ну и конечно, что бы без необходимости не засорять глобальную область видимости, рекомендуется использовать локальные переменные.
  function makecoffe()
  {
      local  myresult='done done done!'
      echo "$myresult"
  }
  result=$(makecoffe)   # или result=`myfunc`
  echo $result          # done done done!
+ Не засоряется глобальная область видимости
- Можно вернуть лишь 1 значение (или несколько разделённых неким разделителем)
Передача косвенной ссылки (Indirect References) в функцию
Самый интересный, функциональный и неуклюжий механизм :)
Это проще 1 раз увидеть, чем 100 раз услышать:
  #!/bin/bash
  # return-val-func-3.sh: Передача косвенной ссылки в функцию.
  echo_var ()
  {
      echo "$1"
  }
  message=Hello
  Hello=Goodbye
  echo_var "$message"        # Hello
  # Теперь давайте передадим в функцию косвенную ссылку.
  # Т.е. дважды разыменуем переменную message
  echo_var "${!message}"     # Goodbye
  echo "-------------"
  # И что же произойдёт, если мы поменяем содержимое переменной "Hello" ?
  Hello="Hello, again!"
  echo_var "$message"        # Hello
  echo_var "${!message}"     # Hello, again!
  exit 0
А теперь по-честному и по порядку
В отличии от некоторых других языков программирования, в bash
сценариях параметры в функцию обычно передаются по значению.
Если имена переменных (которые фактически являются указателями)
будут переданы в функцию в качестве параметров, то они будут обработаны,
как строковые литералы.
Функции обрабатывают свои аргументы, как литералы.
Переменные - это способ представления данных в различных языках программирования. Переменная не более чем метка - имя, назначенное области или группе областей памяти компьютера, где содержатся данные.
Имя переменной - это "контейнер" для хранения значения данных. Обращение к значению (получение значения) переменной называется подстановкой переменной.
Отмечу, что надо различать имя переменной и ее значение. Если variable1 - имя переменной, то $variable1 - обращение к значению, которое она содержит.
Имя переменной - в действительности, ссылка/указатель на область(ти) памяти, где фактические данные связаны со значением, которое они хранят.
  bash$ variable1=23
  bash$ echo variable1
  variable1
  bash$ echo $variable1
  23
Как сказано выше, после подстановки переменной (referencing a variable, $var),
мы получаем значение, на которую указывает переменная.
А как насчёт значения, на которое укзывает само значение? $$var ?
Вы же помните, что с переменными мы работаем просто как с литералами? :)
Ну вот мы и пробуем получить значение, на которое указывает переменная,
имя которой тоже является значением другой переменной.
Немного запутанно, но, надеюсь, уловили.
Настоящая нотация выглядит как \$$var (или, c версии bash 2, как ${!variable})
и обычно используется вместе с eval.
Данный механизм и называется косвенная ссылка (indirect reference).
Пример 1. Для понимания
  #!/bin/bash
  # dereference.sh
  # Разыменовывание параметра переданного в функцию.
  # Сценарий от Bruce W. Clare.
  dereference ()
  {
       y=\$"$1"   # Сохранили имя переменной
       echo $y    # $Junk     == echo \$$y
       x=`eval "expr \"$y\" "`    # $x == Junk
       echo $1=$x
       eval "$1=\"Некоторый Другой Текст \""  # Присваиваем новое значение.
  }
  Junk="Некоторый Текст"
  echo $Junk "до"         # Некоторый Текст до
  dereference Junk
  echo $Junk "после"     # Некоторый Другой Текст после
  exit 0
Пример 2. Практичный
  function myfunc()
  {
      local  __resultvar=$1
      local  myresult='some value'
      eval $__resultvar="'$myresult'"
  }
  myfunc result
  echo $result
Так как имя переменной само хранится в переменной,
мы не можем установить его напрямую. Для этого используют eval.
Если в кратце, eval сообщает bash-интерпретатору, что необходимо
дважды проинтерпретировать строку. В первый раз он установит result='some value',
а во второй - искомую переменную (которую передали как аргумент).
При сохранении имени переменной, которую передали как аргумент командной строки, убедитесь, что сохраняете её локально и в переменную с отличным (по возможности) именем, нежели использовалась в командной строке. Если этого не сделать, и имя переменной, переданной как параметр командной строки, совпадёт с именем локальной переменной, в которую Вы сохраняете значение, то ничего не будет работать :D
Пример 3. Не работающий!
  function myfunc()
  {
      local  result=$1
      local  myresult='some value'
      eval $result="'$myresult'"
  }
  myfunc result
  echo $result
Почему? Ну... в тот момент когда eval совершает вторую подстановку в
result='some value', result - уже локальная переменная функции, поэтому
мы установим её ещё разок, а не искомую переменную.
Для большей гибкости, можно комбинировать разные подходы:
Пример 4. Универсальный
  function myfunc()
  {
      local  __resultvar=$1
      local  myresult='some value'
      if [[ "$__resultvar" ]]; then
          eval $__resultvar="'$myresult'"
      else
          echo "$myresult"
      fi
  }
  myfunc result
  echo $result        # some value
  result2=$(myfunc)
  echo $result2       # some value
Т.е. если в функцию не передано ни одного параметра, значение просто выводится в stdout.
Использованные источники:
- Linux Journal: Returning Values from Bash Functions
- Advanced Bash Scripting по-русски 4. Введение в переменные и параметры, 9. Indirect References, 23. Функции