22

OS: Ubuntu 16.04.3

Shell: Bash 4.3.48


I know that is possible to temporarily change the content of a variable as in var=value command, being probably IFS= read -r var the most notable case of this.

And, thanks to Greg's wiki, I also understand:

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

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

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

What escapes my understanding is this:

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

As far as I know (and following the logic of previous examples), it should print bar, not (bar baz).

Does this only happen to me? Is this the intended behavior and I'm missing something? Or is this a bug?

nxnev
  • 3,654
  • 3
    Perhaps it has something to do with the fact that bash doesn't support arrays as environmental variables? – jesse_b Dec 21 '17 at 18:30
  • 3
    @Jesse_b Maybe. Though when I run export var=(foo bar); echo "${var[0]}" it prints foo, not (foo bar). – nxnev Dec 21 '17 at 18:35
  • 1
    Odd, that worked for me as well. And using export it shows: declare -ax var=([0]="foo" [1]="bar") – jesse_b Dec 21 '17 at 18:39
  • 3
    Environment can't contain arrays, AFAIK. E.g., export i_am_array=(foo bar); /usr/bin/env | grep i_am_array gives no output here. – derobert Dec 21 '17 at 18:47
  • 3
    Also: foo() { declare -p var; } ; var=(bar baz) foo gives declare -x var="(bar baz)" confirming its being treated as a string, not an array – derobert Dec 21 '17 at 18:49
  • @derobert I see. But still export var=(foo bar); declare -p var also prints declare -ax var='([0]="foo" [1]="bar")'. So using declare to check the attributes of var displays both as an array and as a string. It could be that mantainers decided to add support to arrays as env variables but it's still an ongoing work? – nxnev Dec 21 '17 at 18:59
  • 1
    @nxnev It definitely sounds like there is a bug somewhere... There are a bunch of inconsistent behaviors. Environment variables aren't a bash creation, so arrays in env vars aren't really possible. – derobert Dec 21 '17 at 19:00
  • Also try var[0]=blahblah foo – ilkkachu Dec 21 '17 at 19:27
  • @ilkkachu It prints nothing. And if I add declare -p var to foo(), it seems that variable is unset: bash: declare: var: not found. So using different approaches var could be a string, an array or unset. – nxnev Dec 21 '17 at 19:38
  • @nxnev, exactly. And var=(aa bb); var[0]=what foo prints aa, so the assignment to a member of an array doesn't happen at all. – ilkkachu Dec 21 '17 at 19:57
  • @derobert, shells like rc, es, fish support exporting arrays using some form of encoding. – Stéphane Chazelas Dec 21 '17 at 20:09
  • 1
    I found the useful answer about the VAR=VALUE some-command construction. Also added link to it in my answer. – MiniMax Dec 22 '17 at 17:59

3 Answers3

19

Generally calling:

var=value cmd

where cmd is a function is not portable.

With bash, that only works for scalar variables (and with x=(...) parsed as an array but assigned as a scalar) and there are a number of issues with scoping if you do that, with ksh93 and yash, it works but the variable definition remains afterwards. With mksh, you get a syntax error. In the Bourne shell, it didn't work at all, even for scalar variables.

Also note that even with scalar variables, whether the variable ends up being exported within the function (that is, passed to commands being executed) varies from shell to shell (it is in bash, yash, mksh, zsh, but not in ksh, ash).

It only works the way you'd expect with zsh. Note that zsh array indices start at 1.

bash-4.4$ zsh
$ a=(before value)
$ f() echo $a[1]
$ a=(temp value) f
temp
$ echo $a[1]
before
12

It's not just a bug, it seems to be an unimplemented feature with no plans of ever being so. This mailing list post from 2014 has this from the creator:

Fortunately, in bash 4.3 (patchlevel 25), you cannot just -DARRAY_EXPORT and get array variable import/export. The code doesn't compile, and if you fix that, it does not link, and if you fix that, well, you end up with the following issue.

That's a ton of trouble to go through just for this. I don't have any plans to enable array export.

Pulling from the latest git repo for Bash has this in variables.c:

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

Suggesting that whatever is there isn't complete.

  • 5
    Here, it's for a function, so there's no question of exporting anything as there's no execve() system call being involved. See zsh for a shell that supports calling functions with an array temporarily set that way. – Stéphane Chazelas Dec 21 '17 at 19:50
  • @StéphaneChazelas But the environment changes (by adding a new variable) and then reverts to back, after the function completed (I am about this case: my_var=one func_bar). Can we say, that export is adding to the environment and thus, the exporting is used here, under the hood? Look at my answer, I added the demonstration code. – MiniMax Dec 22 '17 at 19:23
10

From the man bash's BUGS section (the version of the bash is 4.3):

BUGS

   Array variables may not (yet) be exported.

The next code demonstrates, that a temporary variable exists in the environment, only while the function is running. When the function is completed, the temporary variable disappears.

### 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

Related information:

MiniMax
  • 4,123