I would be suspicious of your version of Bash to start. I'm on this version and I can set arrays just fine.
$ bash --version
GNU bash, version 4.2.45(1)-release (x86_64-redhat-linux-gnu)
Example
$ array[0]=0
$ array[1]=1
$ array[2]=2
You can then recall them individually:
$ echo ${array[0]}
0
$ echo ${array[1]}
1
$ echo ${array[2]}
2
Or as a list:
$ echo "${array[@]}"
0 1 2
You cannot just call the array as another variable:
$ echo $array
0
I believe this may have been what was misleading you into thinking it wasn't working correctly. Also setting an array element with another variable's value also works.
Example
$ newval=3
$ array[2]=$newval
$ echo "${array[@]}"
0 1 3
You could also use printf
here, doing so will show you why you want to quote the results of ${array[@]}
. But first let's add a string to the array:
$ array[0]="a string"
Without the quotes:
$ printf "%s\n" ${array[@]}
a
string
1
3
With quotes:
$ printf "%s\n" "${array[@]}"
a string
1
3
Passing things around
Well you have 2 tactics that you can make use of within Bash. You can simply set variables so that they're globally scoped. This is often the technique that most people employ.
The other method involves passing by value, values to functions within Bash. I'll show a basic function where I pass it 2 values.
Example
$ function hello() {
printf "I got arg1: %d and arg2: %d\n" $1 $2
}
Now when I execute it with different arguments:
$ hello 1 2
I got arg1: 1 and arg2: 2
$ hello 3 4
I got arg1: 3 and arg2: 4
$ hello 3 blah
bash: printf: blah: invalid number
I got arg1: 3 and arg2: 0
You can read more about arrays and functions within Bash here to get a better understanding of the technologies.
References
I think you're asking two different things there.
Is there a way to make bash print this info without the loop?
Yes, but they are not as good as just using the loop.
Is there a cleaner way to get/print only the key=value portion of the output?
Yes, the for
loop. It has the advantages that it doesn't require external programs, is straightforward, and makes it rather easy to control the exact output format without surprises.
Any solution that tries to handle the output of declare -p
(typeset -p
)
has to deal with a) the possibility of the variables themselves containing parenthesis or brackets, b) the quoting that declare -p
has to add to make it's output valid input for the shell.
For example, your expansion b="${a##*(}"
eats some of the values, if any key/value contains an opening parenthesis. This is because you used ##
, which removes the longest prefix. Same for c="${b%% )*}"
. Though you could of course match the boilerplate printed by declare
more exactly, you'd still have a hard time if you didn't want all the quoting it does.
This doesn't look very nice unless you need it.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
With the for
loop, it's easier to choose the output format as you like:
# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'
# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'
From there, it's also simple to change the output format otherwise (remove the brackets around the key, put all key/value pairs on a single line...). If you need quoting for something other than the shell itself, you'll still need to do it by yourself, but at least you have the raw data to work on. (If you have newlines in the keys or values, you are probably going to need some quoting.)
With a current Bash (4.4, I think), you could also use printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
instead of printf "%q=%q"
. It produces a somewhat nicer quoted format, but is of course a bit more work to remember to write. (And it quotes the corner case of @
as array key, which %q
doesn't quote.)
If the for loop seems too weary to write, save it a function somewhere (without quoting here):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
And then just use that:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
Works with indexed arrays, too:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
Best Answer
Assuming your keys don't contain colons or newlines and your values don't contain newlines:
If you need to arrange the key in the order given by the values, read back the output: