I want to do case insensitive lookup on VAR2 based on the content of VAR1 that holds some parameters in the form key:value. If a VAR1 key is present replace the value in VAR2.

For example for:


I want the result will be:


VAR1 can be empty.

  • none of the results in var3 exist in both var1 and var2; only "liam" exists in both...?
    – Jeff Schaller
    Jun 11, 2018 at 13:18
  • @JeffSchaller If I understand correctly OP wants to do a case insensitive lookup on VAR2 based on the content of VAR1 that holds some parameters in the form key:value. If a VAR1 key is present replace the value in VAR2.
    – oliv
    Jun 11, 2018 at 13:24
  • I will use your frasing, it is clearer.
    – Nir
    Jun 11, 2018 at 13:37
  • What shell are you using for this?
    – Kusalananda
    Jun 11, 2018 at 13:38
  • GNU bash, version 4.1.2(2)-release (x86_64-redhat-linux-gnu)
    – Nir
    Jun 11, 2018 at 13:44

3 Answers 3


With zsh:


typeset -A map
for i ("${(@s(,))VAR1}") map[${(L)i%%:*}]=${i#*:}
for i ("${(@s(,))VAR2}") out+=${(qq)${map[${(L)i}]:-$i}}

printf '%s\n' "$VAR3"



Same with awk (which you could use with zsh, or any Bourne-like shell like bash):


export VAR1 VAR2
awk -v q=\' 'BEGIN {
  n = split(ENVIRON["VAR1"], a, ",")
  for (i = 1; i <= n; i++) {
    k = v = a[i]
    sub(/:.*/, "", k)
    sub(/[^:]*:/, "", v)
    map[tolower(k)] = v
  n = split(ENVIRON["VAR2"], a, ",")
  for (i = 1; i <= n; i++) {
    k = tolower(a[i])
    out = out sep q (k in map ? map[k] : a[i]) q
    sep = ","
  print out

(one difference is that here we only enclose the values inside single quotes. If the value contains single quotes itself, like foo'bar, you get 'foo'bar' while zsh's ${(qq)var} would give you 'foo'\''bar').

The zsh version allows any value for the items. They can contain any byte values including newline and NUL or an empty value. The awk one won't support NULs as those can't be stored in environment variables and depending on the implementation may choke of items containing bytes not forming valid characters.

Note that with both, VAR2= is understood as an empty list while VAR2=, is understood as a list of 2 empty elements, there's no way to express a list of one empty element.

  • Do you have a version of it in bash?
    – Nir
    Jun 11, 2018 at 13:54
  • @Nir, see edit. Jun 11, 2018 at 13:54

Using awk based on RS (record separator):

VAR3=$(awk '
         NR>FNR{printf "%s\047%s\047",(FNR>1?ORS:""),(a[$1]?a[$1]:$1)}
       ' <(echo "$VAR1") <(echo "$VAR2") 

The a array is filled with the content of VAR1 with the key and value pair. This one is used when parsing the second variable and value is replaced if an array entry exists.


A bash (4.0+) solution that builds up a lookup table (an associative array) using the lower-cased keys from VAR1 and the associated values. It then goes through the values in VAR2 and builds VAR3 with values from the lookup table, or from VAR2 if there is no key in the lookup table corresponding to the current VAR2 string.


declare -A lookup

# build lookup table
while read -d , key_value; do
    # $key_value is a string like "tom:rich", separate these into key and value:
    IFS=: read key value <<<"$key_value"

    # add lower-cased key to table with value
done <<<"$VAR1,"

# do lookups in table
while read -d , string; do
    # get newstring from table, but use $string if there's no entry:

    # add $newstring to VAR3, with a delimiting comma unless VAR3 is empty
done <<<"$VAR2,"

printf 'VAR3 = %s\n' "$VAR3"

This code outputs

VAR3 = 'viki','albert','rich'

This assumes that the values in VAR1 and VAR2 contains no newlines.

  • The problems with using IFS=: read x y <<< ... is that it wouldn't work for values that contain newline, and for VAR1='x:foo:bar' you'd get a x -> foo:bar mapping while for VAR1=x:foo: you'd get a x -> foo mapping. Without -r, you'd have unexpected behaviour with backslash. Note that using <<< also implies creating a temporary file each time with the content. Jun 11, 2018 at 14:47
  • Not sure this really saves anything, but you can split VAR1 on comma with IFS=, read -ra pairs <<<"$VAR1", then iterate over the pairs with for var_value in "${pairs[@]}". Jun 11, 2018 at 14:48
  • 1
    @glennjackman, that would split the first line of VAR1 on comma, not the full value of VAR1 if it contains several lines. Jun 11, 2018 at 14:50

You must log in to answer this question.

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