0

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:

VAR1=tom:rich,LIAm:viki
VAR2=liam,albert,tom

I want the result will be:

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

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 '18 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 '18 at 13:24
  • I will use your frasing, it is clearer. – Nir Jun 11 '18 at 13:37
  • What shell are you using for this? – Kusalananda Jun 11 '18 at 13:38
  • GNU bash, version 4.1.2(2)-release (x86_64-redhat-linux-gnu) – Nir Jun 11 '18 at 13:44
3

With zsh:

VAR1=tom:rich,LIAm:viki
VAR2=liam,albert,tom

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

printf '%s\n' "$VAR3"

Output:

'viki','albert','rich'

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

VAR1=tom:rich,LIAm:viki
VAR2=liam,albert,tom

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.

|improve this answer|||||
  • Do you have a version of it in bash? – Nir Jun 11 '18 at 13:54
  • @Nir, see edit. – Stéphane Chazelas Jun 11 '18 at 13:54
0

Using awk based on RS (record separator):

VAR3=$(awk '
         BEGIN{RS="[,\n]";FS=":";ORS=","} 
         NR==FNR{a[tolower($1)]=$2}
         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.

|improve this answer|||||
0

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.

VAR1=tom:rich,LIAm:viki
VAR2=liam,albert,tom

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
    lookup[${key,,}]=$value
done <<<"$VAR1,"

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

    # add $newstring to VAR3, with a delimiting comma unless VAR3 is empty
    VAR3+="${VAR3:+,}'$newstring'"
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.

|improve this answer|||||
  • 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. – Stéphane Chazelas Jun 11 '18 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[@]}". – glenn jackman Jun 11 '18 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. – Stéphane Chazelas Jun 11 '18 at 14:50

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.