Сохранить возвращаемое значение и запускать не из подоболочки

Я вызываю некоторую функцию, которая устанавливает VARIABLE в какое-то значение и возвращает другое значение. Мне нужно сохранить значение VARIABLE и присвоить возвращаемое значение функции другому VAR. Вот что я попробовал:

bar() {
        VAR="$(foo)"
        echo $VARIABLE >&2
        echo $VAR >&2
}

foo() {
        VARIABLE="test"
        echo "retval"
}
bar

Но он печатает

retval

Есть ли способ сделать это?


person Some Name    schedule 19.05.2018    source источник


Ответы (3)


ksh имеет для этого удобную конструкцию подстановки команд без подоболочки:

#!/bin/ksh
foo() {
  echo "cat"
  variable="dog"
}
output="${ foo }"
echo "Output is $output and the variable is $variable"

В bash и других оболочках вместо этого вам нужно использовать временный файл:

#!/bin/bash

foo() {
  echo "cat"
  variable="dog"
}

# Create a temp file and register it for autodeletion
file="$(mktemp)"
trap 'rm "$file"' EXIT

# Redirect to it and read it back
foo > "$file"
output="$(< "$file")

echo "Output is $output and the variable is $variable"
person that other guy    schedule 19.05.2018
comment
Не в баше вообще, нет. Однако часто есть способы написать об этом. Например, если вам нужна переменная только для третьей команды, чьи побочные эффекты вас не волнуют, вы можете сделать что-то вроде output="$(foo; echo "Variable was set to $variable" >&2)" - person that other guy; 19.05.2018

Ну, это может быть некрасиво, но

В bash все переменные являются глобальными, если они не объявлены local. Поэтому вы можете назначить VAR прямо в foo:

bar() {
        #### VAR="$(foo)"
        foo                  # Just call it
        echo $VARIABLE >&2
        echo $VAR >&2
}

foo() {
        VARIABLE="test"
        #### echo "retval"
        VAR="retval"         # What would have gone in $VAR before
}
bar

Выход:

test
retval
person cxw    schedule 19.05.2018

BASH не возвращает значение вызывающей стороне, как это делают традиционные языки функционального программирования. Единственное, что могут возвращать функции BASH (по сути), это «коды выхода» в результате функции.

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

Вот набор функций с некоторыми операторами эха вокруг него, которые могут помочь вам понять это:

#!/bin/bash

export VARIABLE=""

bar() {
        echo ">>> In function bar() ... "
        echo "        _var :: $_var"
        echo "    VARIABLE :: $VARIABLE"

        echo ">>> Setting '_var' to value 'local to bar' ... "
        local _var="local to bar"

        echo ">>> calling function foo() ... "
        foo
        echo ">>> Back in function bar() ... "

        echo "        _var :: $_var"
        echo "    VARIABLE :: $VARIABLE"
}

foo() {
        echo ">>> In function foo() ... "

        local _var="local to foo"
        VARIABLE="I'm a global variable"

        echo "        _var :: $_var"
        echo "    VARIABLE :: $VARIABLE"
}
bar

В приведенном выше примере мы установили переменную «_var» как локальную в области видимости. Когда «bar()» работает, мы устанавливаем значение локального «_var» как «локальное для бара». Впоследствии мы вызываем функцию "foo()"; который устанавливает локальную переменную "_var" в значение "local to foo".

Мы также устанавливаем глобальную переменную «VARIABLE» в значение «Я — глобальная переменная».

Когда выполнение возвращается обратно к функции «bar()», мы печатаем наши VARIABLE и _var. VARIABLE приводит к строке «Я глобальная переменная».

Подробнее о возвращаемых значениях функций в BASH см. в вопросе возвратное значение в функции Bash. .

Вот результаты вышеописанного:

>>> In function bar() ...
        _var ::
    VARIABLE ::
>>> Setting '_var' to value 'local to bar' ...
>>> calling function foo() ...
>>> In function foo() ...
        _var :: local to foo
    VARIABLE :: I'm a global variable
>>> Back in function bar() ...
        _var :: local to bar
    VARIABLE :: I'm a global variable

ЗАМЕТЬТЕ, что мы никогда не устанавливаем значение VARIABLE в функции «bar()». Он устанавливается в «foo()», и впоследствии мы получаем доступ к результатам с помощью глобальной переменной.

Локальные переменные "_var" также показывают, что значения являются "локальными" только для каждой функции, и если указать, что они локальные, они не имеют значения или значения вне самой функции.

Вы можете использовать суб-оболочку, чтобы делать то, что вы хотите. Однако подоболочки работают в отдельном пространстве процесса, поэтому часто возникают непреднамеренные побочные эффекты. Для использования в примере подоболочки:

#!/bin/bash

export VARIABLE="default global value"

function bar()
{
    VAR=$(foo)
    echo "VARIABLE :: $VARIABLE"
    echo "     VAR :: $VAR"
}

function foo()
{
    # in theory, VARIABLE is globally scoped, but called in a
    # subshell, it won't impact/change the calling environment
    export VARIABLE='test'
    echo "$VARIABLE"
}

bar

Хотя захват вывода через подоболочку таким образом работает - если ваша функция возвращает или выводит более одного значения, вы можете столкнуться со странным поведением, которого не планировали. Приведенный выше пример показывает, что случайное наблюдение за функциями указывает на то, что "foo()" изменит значение "VARIABLE" в глобальной области видимости. Но поскольку он запускается в подоболочке ('$(foo)') - он фактически вообще не меняет глобальное значение VARIABLE.

Это может привести к тонким логическим ошибкам в больших сценариях, и я бы не советовал использовать этот метод. Вот вывод приведенного выше фрагмента:

VARIABLE :: default global vaule
     VAR :: test
person sygibson    schedule 19.05.2018