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.


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 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 at 17:16
  • The paste solution only works if none of the elements contain newlines: configs[oops]=$'first\nsecond' Mar 26 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 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>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.