Reputation: 71
If I wanted to pass the keys and values of an associative array in bash separately, and used something like
./foo.py -k "${!args[@]}" -v "${args[@]}"
would they come out in the same order? I don't really care what other the k=v pairs are stored in, but I do need to know if I could count on the keys and values coming out such that the 3rd item in the key array is in fact the key for the 3rd item in the value array.
I know that associative arrays are "unordered" and that whatever order you add them to the array is irrelevant to how they're output, but I'm wondering if the underlying storage behavior means they will always output in the same order.
Upvotes: 7
Views: 942
Reputation: 1
Arrays are not the solution to this problem, especially not associative arrays. Even if they did come out in the same order, you would have multiple keys for a single -k option, resulting in syntax error. Also arrays are a Bashism, and not defined by POSIX. A better solution would be something like this:
./foo.py -k key1,key2,key3 -v val1,val2,val3
Then surely Python can split the input strings? I did something similar with POSIX shell:
tr , '\n' > keys <<eof
$1
eof
tr , '\n' > vals <<eof
$2
eof
paste -d '\n' keys values |
while
read key
read val
do
printf 'key: %s, val: %s\n' "$key" "$val"
done
Of course this would be much easier with Python, as you could just split the strings directly into arrays without using files.
Upvotes: 0
Reputation: 114330
It appears that the answer is yes, the keys and values will always be in the same order, based on the code I found in Bash version 4.3, assoc.c
, available here. The keys and values of the array are retrieved by the functions assoc_keys_to_word_list
and assoc_to_word_list
, respectively. Both of these functions delegate to assoc_to_word_list_internal
, which runs the same loop in both cases, and only differentiates the type of item being retreived based on the t
parameter (lines 482-503):
static WORD_LIST * assoc_to_word_list_internal (h, t) HASH_TABLE *h; int t; { WORD_LIST *list; int i; BUCKET_CONTENTS *tlist; char *w; if (h == 0 || assoc_empty (h)) return((WORD_LIST *)NULL); list = (WORD_LIST *)NULL; for (i = 0; i < h->nbuckets; i++) for (tlist = hash_items (i, h); tlist; tlist = tlist->next) { w = (t == 0) ? (char *)tlist->data : (char *)tlist->key; list = make_word_list (make_bare_word(w), list); } return (REVERSE_LIST(list, WORD_LIST *)); }
In case you are wondering, make_word_list
is defined in array.c/h
. It just appends a new WORD_LIST
node to the existing linked list.
While this provides no contractual guarantee that the behavior you expect will always be supported, it is a pretty good indication that you can use your calling convention safely, at least for now. The fact that associative arrays are Bash-only makes the implementation more of a valid reference.
Upvotes: 2