1
for key in ${!current_file[@]} 
    do
      echo $key      
     done

I declare current_file like below in bash:

declare -A current_file

insert key as a file and size as value in current_file. Output of the for loop prints:

file2
file1

I want to print like:

file1 
file2

How can I print like this?

2 Answers 2

2

To print the list of keys sorted alphabetically, and assuming GNU sort, you could do:

printf '%s\0' "${!hash[@]}" | sort -z | tr '\0' '\n'

Or to iterate over the sorted list of keys:

while IFS= read -rd '' -u3 key; do
  something with "${hash[$key]}"
done 3< <(printf '%s\0' "${!hash[@]}" | sort -z)

If you can guarantee the keys won't contain newline characters, you can simplify it to:

printf '%s\n' "${!hash[@]}" | sort

With zsh, that would be:

printf "%s\n" "${(ko@)hash}"

(k to get the keys, o to order that list, @ inside double quote to preserve the empty key if any (note that bash currently has a limitation in that it can't have a hash element with an empty key)).

And to loop over them:

for key in "${(ko@)hash}"; do
  something with "$hash[$key]"
done

Note that except for that last one above, we're assuming the hash contains at least one element (as printf '%s\0' with no argument would still output \0 as if there was an element with an empty key).

In any case, writing ${!current_file[@]} unquoted makes very little sense as that's invoking the split+glob operator on that list of keys.

2
  • I'm curious to know why you used fd3 instead of fd0 for input to the read statement. I wasn't familiar with the -u flag to read, and as I would have simply redirected using stdin, I am wondering about the benefits of your approach. Thanks.
    – user001
    Aug 15, 2017 at 0:17
  • 1
    @user001, the something inside the loop may need to do something with stdin, while there's no reason they would expect anything on their fd 3. Actually, it would be even better (neater/cleaner) to close that fd for it (adding 3<&-). Aug 15, 2017 at 10:21
1

Associative array in bash is like hashes/dictionaries in typical languages, and like them it is unordered (ordered according to internal hash value actually). So you can't expect the output to be (generally) sorted while iterating over the (associative) array keys.

You need to sort it yourself, for example sending the STDOUT to the STDIN of sort:

for key in ${!current_file[@]}; do
    echo $key      
done | sort 

or something similar.


If you want to keep an order so that you can do any order based operation on the associative array keys/values, you can keep another indexed array as a reference of insertion. The following should give you a basic idea:

## Associative array `foo`
$ declare -A foo=([spam]=egg [baz]=cool)

## Reference indexed array `bar` containing the keys of `foo` sequentially indexed
$ bar=(spam baz)

## Retrieving in forward order
$ for i in "${bar[@]}"; do echo "$i :: ${foo[$i]}"; done
spam :: egg
baz :: cool

## Retrieving in reverse order
$ for ((i=${#bar[@]}-1; i>=0; i--)); do idx="${bar[$i]}"; echo "$idx :: ${foo[$idx]}"; done
baz :: cool
spam :: egg
2
  • he wants reverse.. your code will sort it
    – shas
    Dec 19, 2016 at 6:57
  • @shas Check edits.
    – heemayl
    Dec 19, 2016 at 7:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .