Is it possible to declare and export a bash array from a single statement within a function?
My current workaround is to first declare, then to export.
f() { foo=(1 2 3); export foo; }; f; export -p | grep foo=
declare -ax foo='([0]="1" [1]="2" [2]="3")'
I observe that:
f() { export bar=(1 2 3); }; f; export -p | grep bar=
<no output>
and:
f() { export baz="(1 2 3)"; }; f; export -p | grep baz=
declare -x baz="(1 2 3)" # not an array
I use bash v3.2.48(1)-release and can't upgrade.
Some background:
I have a friend with whome I am trying to study "some" Django.
He's more clueless than me at the command line and needs the following, on OSX hackintosh:
On Windows, I would alias a command shortcut, like cmd.exe, set custom environment variables and start ipython. This works: after exiting ipython one still finds oneself in a command interpreter.
Is this possible at all with OSX's standard bash? I played with bash -c but many things don't work, like changing into a directory, being able to exit python and stay in a terminal, etc. Also played with -s.
You can't export an array in Bash (or any other shell). Bash will never export an array to the environment (until maybe implemented someday, see the bugs
section in the manpage).
There are a couple things going on here. Again, setting the -x
attribute on an array is nonsensical because it won't actually be exported. The only reason you're seeing those results is because you defined the array before localizing it, causing it to drop down to the next-outermost scope in which that name has been made local (or the global scope).
So to be clear, whenever you use a declaration command, you're always creating a local except when you use export
or readonly
with a non-array assignment as an argument, because these are POSIX, which doesn't specify either locals or arrays.
function f {
typeset -a a # "a" is now local to "f"
g
printf 'Now back in "f": %s\n' "$(typeset -p a)"
}
function g {
a=(1 2 3) # Assigning f's localized "a"
typeset -a a # A new local a
printf 'In "g": %s\n' "$(typeset -p a)" # g's local is now empty.
a=(a b c) # Now set g's local to a new value.
printf 'Still in "g": %s\n' "$(typeset -p a)"
}
f
# In "g": declare -a a='()'
# Still in "g": declare -a a='([0]="a" [1]="b" [2]="c")'
# Now back in "f": declare -a a='([0]="1" [1]="2" [2]="3")'
It seems if you give an array as an argument to export
then Bash will make it local like any other declaration command. Basically don't use export
to define an array. export
is a POSIX builtin and its behavior is undefined for arrays, which is probably why bash just treats it as though you had used typeset -ax a=(1 2 3)
. Use typeset
, local
, or declare
. There's really no way to know what's "correct", or even compare with other shells. ksh93 is the only other that accepts array assignments as arguments to declaration commands, and its behavior doesn't agree with Bash.
The important thing to understand is that the environment has nothing to do with it, you're just playing with the quirks of locals when trying to do nonstandard things with POSIX-only commands.
In order to actually get the effect you want, you may use typeset -p
.
function f {
typeset -a "${1}=(1 2 3)"
typeset -p "$1"
}
typeset -a arr
eval "$(f arr)"
typeset -p arr
Bash guarantees you'll get out the correct result, but I find this isn't very useful and rarely use this approach. It's usually better to just let the caller define a local and use dynamic scope do the work (as you've already discovered... just do it without exporting).
In Bash 4.3 you can use typeset -n
, which is really the most correct way to handle this.
The first option seems to be the only one that's going to work. I experimented with bash
4.2 as well as 3.2.48. The interesting information, to me, were these minor variants of your examples:
$ f() { declare -a bar=(1 2 3); export bar; export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ f() { export bar=(1 2 3); export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ f() { bar=(1 2 3); export bar; export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ unset bar
$ f() { bar=(1 2 3); }; f; set | grep bar=
bar=([0]="1" [1]="2" [2]="3")
bar=(1 2 3)
$
In these examples, I test the export
inside the function as well as outside the function. Because the variables are being defined in the function, they appear to be scope-limited to the function. The exception is when the variable is defined before any attributes are applied — the last two functions. There a global variable is created, and then exported (in one case).
So, if you're going to get the array exported from the function, you have to create it without the declare
or export
statements (because those make the variable local to the function), and then export it.
I hope that explains it; I can see fuzzily what's going on and it makes sense, after a fashion. I'm not sure I explained it as well as it should be explained.
In the declare
section of the bash
manual, it says:
When used in a function,
declare
makes each name local, as with thelocal
command.
There isn't equivalent wording in the export
. However, the observed behaviour is as if export
is implemented by declare -x
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With