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

Давайте представим, написали ли вы функцию, которая возвращает строку, например:

#!/bin/bash
function hello() {
    return "Hello $1"
}
echo $(hello world)

Оператор return не работает:

The return statement doesn’t work:
test.sh: line 5: return: Hello world: numeric argument required

Это связано с тем, что ключевое слово return в bash предназначено для функции, возвращающей статус выхода. Но как вернуть что-нибудь осмысленное из функции bash? Обычный способ возврата вещей с помощью функций — это STDOUT (стандартный вывод):

#!/bin/bash
function hello() {
  echo "Hello $1"
}
echo $(hello world)

Что бы сработало. Но рассмотрите эти:

  1. Как бы вы вернули несколько переменных?
  2. Как бы вы вернули массив строк, некоторые из которых содержат пробел?
  3. Как бы вы вернули несколько переменных, одна из которых является массивом, который может содержать пробелы?

Конечно, это второстепенный случай. Большинство людей возразят: «Какого черта мне это нужно?» И они абсолютно правы. Но предположим, что мир вот-вот взорвется, если вы не сможете сделать это с помощью функции bash, что вы могли бы сделать?

В ПОРЯДКЕ. Я знаю, что вы можете возиться с глобальными переменными. Но все мы знаем, что функция, имеющая побочный эффект для глобальной переменной с фиксированным именем, воняет. И даже если бы ты сделал это, когда нужно, внутри тебя умирает частичка…

Так что ты можешь сделать?

(Барабанный бой…)

Входит объявлять.

Команда bash «declare» (или ее синонимы «typeset») — это встроенная команда bash, позволяющая изменять свойства переменных. Нас интересуют параметры -n:

-n
Дайте каждому имени атрибут nameref, сделав его ссылкой имени на другую переменную. Эта другая переменная определяется значением имени. Все ссылки, присвоения и изменения атрибутов имени, за исключением тех, которые используют или изменяют сам атрибут -n, выполняются для переменной, на которую ссылается значение имени. Атрибут nameref нельзя применять к переменным массива.

По сути, его можно использовать для передачи по ссылке:

#!/bin/bash
function hello {
    declare -n OUTPUT=$2
    OUTPUT="Hello $1"
}
hello "world" MSG && echo "$MSG"

Это создаст локальную переменную $OUTPUT в качестве ссылки на переменную $MSG (предоставляется здесь для 2-го аргумента $2). Переменная $OUTPUT существует только в локальной области видимости функции. Таким образом, никакая глобальная переменная не разбрасывается.

Поскольку здесь мы не используем ни return, ни STDOUT, мы все равно можем использовать return для обработки ошибок. Такие как:

#!/bin/bash
function find_id_in_restful_api {
    local API_TOKEN=$1
    local SEARCH=$2
    declare -n RETURN_ID=$3
    local RESPONSE=$(curl \
        -X POST \
        -H "Authorization: token $API_TOKEN" \
        -H "Accept: application/json" \
        -d "{\"search\": \"$SEARCH\"}" \
        "https://api.foobar.com/someEntity")
    local ID=$(some_cli_json_parser $RESPONSE)
    if [ -z "$ID" ]; then
      # Oh no. The exepcted id field is not here. Signal error
      return 1
    else
      RETURN_ID=$1
      return 0
    fi
}
if find_id_in_restful_api "some token" "some search term" ID; then
    echo "ID found! $ID"
    # Do some operation with $ID
    # ...
    # ...
else
    echo "Error: ID not found!"
    exit 1
fi

Аккуратно, верно?

Скажите мне, что вы думаете в комментариях.