Bash associative array ordering

If I wanted to pass the keys and values ​​of an associative array in bash separately and use something like

./foo.py -k "${!args[@]}" -v "${args[@]}" 

will they come out in the same order? I don’t care that other k = v pairs are stored, but I need to know if I can count on the keys and the values ​​so that the 3rd element in the key array is actually the key for the 3rd element in the array of values.

I know that associative arrays are β€œunordered” and that any order you add to the array has nothing to do with how they are displayed, but I wonder if the behavior of the underlying storage means that they will always be displayed in the same order .

+6
source share
2 answers

It seems like 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 assoc_keys_to_word_list and assoc_to_word_list respectively. Both of these functions are delegated to assoc_to_word_list_internal , which executes the same cycle in both cases and only differentiates the type of the returned element based on the parameter t (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 *)); } 

If you're interested, make_word_list is defined in array.c/h . It simply adds a new WORD_LIST node to the existing linked list.

Although this does not guarantee a contractual guarantee that the behavior you expect will always be supported, it is a pretty good sign that you can safely use your calling convention, at least for now. The fact that associative Bash arrays only makes the implementation a more reliable reference.

+1
source

Arrays are not a solution to this problem, especially non-associative arrays. Even if they exit in the same order, you will have multiple keys for single -k, which leads to a syntax error. Also arrays are a Bashism , and are not defined by POSIX. A better solution would be something like this:

 ./foo.py -k key1,key2,key3 -v val1,val2,val3 

Then of course Python can break the input lines? I did something similar with the 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, that would be a lot easier with Python, since you could just split strings directly into arrays without using files.

0
source

Source: https://habr.com/ru/post/1013309/


All Articles