Почему переменные bash не являются глобальными, если исходный сценарий выполняется в функции?

Обычно, получая файл из другого скрипта, я могу получить доступ к его переменным.

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

FUNCTION Variables local to the function may be declared with the local builtin command. Ordinarily, variables and their values are shared between the function and its caller.

source filename [arguments] Read and execute commands from filename in the current shell environment

Бывает со всеми моими удобно доступными версиями: 3.2.57 (1) -release (x86_64-apple-darwin17), 4.3.42 (1) -release (x86_64-suse-linux-gnu) и версия 4.3.48 (1) -release (x86_64-pc-linux-gnu)

test-sourced.sh:

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL
declare -x FOO=bar
foo() { echo funfoo=$FOO $$ $SHLVL ; }

test-top.sh:

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL

funcsource () { source ./test-sourced.sh ; }
echo ==== funcsource...
funcsource
echo foo=$FOO
foo

echo ==== source...
source ./test-sourced.sh
echo foo=$FOO
foo

Я вижу этот вывод, но ожидал, что и funcsource, и source будут делать одно и то же:

$ ./test-top.sh 
./test-top.sh 1234 2
==== funcsource...
./test-sourced.sh 1234 2
foo=
funfoo= 1234 2
==== source...
./test-sourced.sh 1234 2
foo=bar 1234 2
funfoo=bar

Это тот же PID и тот же уровень оболочки, поэтому это похоже на преднамеренное поведение. Это ошибка или я что-то упускаю?


Обновление: повторение $ FOO и запуск 'foo' в функции сразу после команды source ДЕЙСТВИТЕЛЬНО выдают свои значения, поэтому они зашли так далеко, но по какой-то причине остаются локальными по отношению к области действия функции. Что до сих пор кажется противоречащим руководству.

4
0
1 875
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Причина такого поведения в том, что вы используете встроенную оболочку declare. Согласно help declare:

When used in a function, declare makes NAMEs local, as with the local command. The -g option suppresses this behavior.

Изменение test-sourced.sh для использования declare -g вместо declare -x (экспорт переменной в среду) - или обычного назначения переменной оболочки - должно показать ожидаемое поведение (где переменная является глобальной):

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL

# Simple shell variable assignment (global by default)
FOO=bar

# Use declare to globally assign a value to the shell variable
declare -g FOO=bar

foo() { echo funfoo=$FOO $$ $SHLVL ; }

Экспорт переменной в среду добавляет полезности только в том случае, если вы хотите, чтобы переменная была доступна для будущих дочерних процессов, запускаемых из этого сеанса оболочки. Если это то, что вы хотите, вы можете использовать любую из следующих конструкций Bash, чтобы переменная была как глобальной, так и экспортированной:

export FOO=bar
declare -x -g FOO=bar

Спасибо. Обычно я никогда не использую declare, но кто-то из моей команды стал евангелистом по этому поводу, и я предположил, что declare -x - это то же самое, что и export.

android.weasel 12.04.2018 16:49

Используйте export вместо declare -x, объявите ограничение объема переменной, чтобы функция не была видна снаружи.

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL
export FOO=bar  #or FOO=bar 
foo() { echo funfoo=$FOO $$ $SHLVL ; }

Другие вопросы по теме