Возможная ошибка в Bash ?: foo () {echo «$ {var [0]}»; }; var = (bar baz) foo

22

ОС : Ubuntu 16.04.3

Оболочка : Bash 4.3.48


Я знаю, что возможно временно изменить содержимое переменной, как var=value command, вероятно, IFS= read -r varнаиболее заметный случай этого.

И, благодаря вики Грега , я также понимаю:

# Why this
foo() { echo "$var"; }
var=value foo

# And this does work
var=value; echo "$var"

# But this doesn't
var=value echo "$var"

Что ускользает от моего понимания, так это:

$ foo() { echo "${var[0]}"; }
$ var=(bar baz) foo
(bar baz)

Насколько я знаю (и следуя логике предыдущих примеров), его следует печатать bar, а не (bar baz).

Это случается только со мной? Это предполагаемое поведение, и я что-то упустил? Или это ошибка?

nxnev
источник
3
Возможно, это связано с тем, что bash не поддерживает массивы как переменные среды?
Jesse_b
3
@Jesse_b Возможно. Хотя, когда я запускаю export var=(foo bar); echo "${var[0]}"это печатает foo, нет (foo bar).
nxnev
1
Странно, это сработало и для меня. И используя exportэто показывает:declare -ax var=([0]="foo" [1]="bar")
Jesse_b
3
Окружение не может содержать массивы, AFAIK. Например, export i_am_array=(foo bar); /usr/bin/env | grep i_am_arrayздесь ничего не выводится.
Дероберт
3
Также: foo() { declare -p var; } ; var=(bar baz) fooдает declare -x var="(bar baz)"подтверждение того, что он рассматривается как строка, а не как массив
derobert

Ответы:

19

Вообще звонит:

var=value cmd

где cmdфункция не является переносимой.

С bash, это работает только для скалярных переменных (и с x=(...)проанализированным как массив, но назначенным как скаляр), и есть ряд проблем с областью видимости, если вы сделаете это, с ksh93и yash, это работает, но определение переменной остается после. С mksh, вы получите синтаксическую ошибку. В оболочке Bourne он вообще не работал, даже для скалярных переменных.

Также обратите внимание, что даже со скалярными переменными то, будет ли переменная в конечном итоге экспортирована в функцию (то есть передана выполняемым командам), варьируется от оболочки к оболочке (это в bash, yash, mksh, zsh, но не в ksh, ясень).

Это работает только так, как вы ожидаете zsh. Обратите внимание, что zshиндексы массива начинаются с 1.

bash-4.4$ zsh
$ a=(before value)
$ f() echo $a[1]
$ a=(temp value) f
temp
$ echo $a[1]
before
Стефан Шазелас
источник
12

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

К счастью, в bash 4.3 (уровень исправления 25) вы не можете просто -DARRAY_EXPORT и получить импорт / экспорт переменной массива. Код не компилируется, и если вы исправите это, он не будет ссылаться, и если вы это исправите, то в итоге вы столкнетесь со следующей проблемой.

Это куча проблем, чтобы пройти только для этого. У меня нет планов включить экспорт массивов.

Вытащить из последнего git-репо для Bash это variables.c:

  #  if ARRAY_EXPORT
        /* Array variables may not yet be exported. */

Предположение, что все, что там, не полно.


источник
5
Здесь речь идет о функции, поэтому нет необходимости экспортировать что-либо, поскольку здесь не execve()задействованы системные вызовы. Смотрите zshдля оболочки, которая поддерживает вызов функций с массивом, временно установленным таким образом.
Стефан
@ StéphaneChazelas Но среда меняется (добавляя новую переменную) и затем возвращается к предыдущей, после завершения функции (я об этом случае:) my_var=one func_bar. Можно ли сказать, что exportэто добавляет к окружающей среде и, таким образом, экспорт используется здесь, под капотом? Посмотрите на мой ответ, я добавил демонстрационный код.
MiniMax
10

Из man bashраздела «Ошибки» (версия bash4.3):

ОШИБКИ

   Array variables may not (yet) be exported.

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

### defining the "bar" function
### it pass all environment variables to the "grep" command
### and the "grep" prints the only "my_var" variable from it
bar() { env | grep my_var=; }

### calls the "bar" function with the temporary 
### variable "my_var" created and assigned.
my_var=one bar

my_var=one         ### The output. The environment contains the "my_var" variable

### checks, does the environment still have the "my_var" variable
### (It doesn't have.)
env | grep my_var=
                   ### The output is empty,
                   ### the environment doesn't contain the "my_var" variable

Связанная информация:

MiniMax
источник