bashshellzshoh-my-zsh

Can I loop over the keys of an associative array with the same syntax as bash and zsh


I want to loop over the keys of an associative array, but I would like my script to work in zsh and bash. Would there be a way of doing it (syntax) that it would work in both

I know this

zsh:

for k in "${(k)array[@]}" do echo $k; done

bash:

for k in "${!array[@]}" do echo $k; done

Btw. I actually use oh-my-zsh, so I cannot really use something as emulate ksh, because it makes my terminal crash.


Solution

  • You can wrap each implementation in a function, and define only the function appropriate to the current shell:

    if [[ $ZSH_VERSION ]]; then
      keys_for_array() {
        local array=$1 dest=$2
        [[ $1 && $2 ]] || { echo "Usage: keys_for_array source-array dest-array" >&2; return 1; }
        : ${(AP)dest::=${(kP)array}}
      }
    elif [[ $BASH_VERSION && ! $BASH_VERSION =~ ^([123][.]|4[.][012]) ]]; then
      keys_for_array() {
        [[ $1 && $2 ]] || { echo "Usage: keys_for_array source-array dest-array" >&2; return 1; }
        local -n array=$1 dest=$2
        eval 'dest=( "${!array[@]}" )'
      }
    else
      keys_for_array() { echo "ERROR: keys_for_array not available for this shell" >&2; return 1; }
    fi
    
    [[ $ZSH_VERSION ]] && typeset -A aa=( 1 one 2 two )
    [[ $BASH_VERSION ]] && declare -A aa=( [1]=one [2]=two )
    keys_for_array aa aak
    
    declare -p aak
    

    ...when run on bash, output is:

    declare -a aak=([0]="1" [1]="2")
    

    ...when run on zsh, output is:

    typeset -a aak=( 1 2 )
    

    In either case, you can then iterate over them:

    keys_for_array aa aak
    for key in "${aak[@]}"; do
      echo "Key $key has value ${aa[$key]}"
    done
    

    Note that above, both functions are syntactically valid in both shells. Otherwise, it might be necessary to use eval or source to pull them in only conditionally. Personally, I would create two separate files with both bash and zsh versions of your portability library, and source only the file appropriate to the current shell.