7

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.

4
  • 1
    Not an answer, because I would have to audit the code to do provide one. However, I'd be greatly surprised if this weren't so, if only because it would take seemingly unnecessary effort to produce different orderings (as long as the array isn't modified between expansions). Commented Dec 19, 2016 at 21:02
  • @chepner: You can't draw this conclusion. I do not know about bash, but in other languages, some keep the order and some don't, depending on the implementation. In others, this might even change between versions. For example, Tcl - until (I think) version 8.5 - guaranteed that the order is kept, but they changed it afterwards. Commented Dec 20, 2016 at 5:19
  • 1
    Are you claiming Tcl performs something like hash randomization on each access? That array get name can return a different list each time it is called if name is not modified at all between calls? Commented Dec 20, 2016 at 12:50
  • You should probably select an answer sometime. Commented Dec 20, 2016 at 18:58

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

3 Comments

This is actually a good use of arrays; their absence is one of the more glaring omissions from the POSIX specification.
@chepner I admit learning that array are not POSIX was a blow - sort of like learning that synchronous JavaScript is deprecated
Compared to that, no multiline lambdas in Python is heaven.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.