Можлива помилка в Bash ?: foo () {echo “$ {var [0]}”; }; var = (bar baz) foo


22

ОС : Ubuntu 16.04.3

Оболонка : Баш 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).

Це трапляється тільки зі мною? Це цілеспрямована поведінка, і я щось пропускаю? Або це помилка?


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не дають результатів.
derobert

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, ви отримуєте синтаксичну помилку. У оболонці Борна вона взагалі не працювала, навіть для скалярних змінних.

Також зауважте, що навіть при скалярних змінних, незалежно від того, чи зміна закінчується експортом у межах функції (тобто передається командам, що виконуються), змінюється від оболонки до оболонки (це в 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 (patchlevel 25) ви не можете просто -DARRAY_EXPORT та отримати імпорт / експорт змінної масиву. Код не компілюється, і якщо ви виправите це, він не посилається, і якщо ви виправите це, ну ви закінчите з наступною проблемою.

Це безліч проблем, щоб пройти саме заради цього. У мене немає планів включити експорт масиву.

Для останнього gpo repo для 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'BUGS' (версія 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

Довідкова інформація:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.