I'm trying to echo all elements of an array on a newline and also with an index number next to it. So that I can take some input and then select an array element to open with a text editor. (they're all config files).

i'm currently doing this:

declare -a configs=(
... )

printf "%s\n"  "${configs[@]}"

which has the output of each config path, but i'm not sure how I'd go about appending the index number to the start of each element.


3 Answers 3


Just loop over the array:

$ declare -A configs=( [vimrc]="~/.vimrc" [foo]="bar" [baz]="bad" )
$ for i in "${!configs[@]}"; do printf '%s: %s\n' "$i" "${configs[$i]}"; done
foo: bar
baz: bad
vimrc: ~/.vimrc

Or, if you want to use the same basic approach, and if none of the values or keys in the array have newlines, try:

declare -A configs=( [vimrc]="~/.vimrc" [foo]="bar" [baz]="bad"  )

  paste \
    <(printf '%s\n' "${!configs[@]}" ) \
    <(printf '%s\n' "${configs[@]}" )

I am assuming that bash will always keep the same order for ${!configs[@]} and ${configs[@]}, but I do not know this for a fact, which is why I personally would feel more comfortable looping over the array explicitly so as to be 100% sure that the values are correct.

  • It's hard to imagine why the keys would come off in a different order than the values, provided the array isn't modified in the meanwhile, but it's not like the manual explicitly says so. Anyway, why the subshell?
    – ilkkachu
    Mar 26, 2021 at 17:13
  • @ilkkachu precisely. I think it's a safe assumption but I don't like needing to assume and since I haven't found anything suggesting that bash ensures the order will be kept, I felt it worth pointing out. As for the subshell, no reason other than inertia. I copied the OP's code, then realized the IFS was not doing anything useful in my version and removed it, but forgot to remove the subshell. Gone now, thanks.
    – terdon
    Mar 26, 2021 at 17:16
  • The paste solution only works if none of the elements contain newlines: configs[oops]=$'first\nsecond' Mar 26, 2021 at 20:23

okay, i got it just by looping over the array. wasn't sure if there was a better/cleaner solution though.

my fix:

declare -a configs=(
... )

for o in "${configs[@]}"
  printf "%d. %s\n" $((i++)) $o
  • 1
    This is completely different to your original question. Why are you now using indexed arrays instead of associative arrays? This is needlessly complicated too, you could just do for((i=0;i<${#configs};i++)); do printf "%d. %s\n" "$i" "${configs[i]}"; done, no need for extra variables.
    – terdon
    Mar 26, 2021 at 17:30

It may be as simple as using nl (number lines):

$ configs=( first second third )
$ printf '%s\n' "${configs[@]}"  | nl
1 first
2 second
3 third

Assuming that elements of the array contain no newline(s).


But, in bash, selecting from a list of items is a job for the builtin command select:

$ select pen in "${configs[@]}"; do echo "got it <$pen>"; [[ ${pen:+set} ]] && break; done

1) first
2) second
3) third
#? 2

got it <second>

You must log in to answer this question.

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