开发者

bash - determine a substring in one variable when an associated substring is known in another variable

开发者 https://www.devze.com 2023-04-09 03:23 出处:网络
Assuming I had two strings, one acts a string of space delimited keys, and another acts as the associative space-delimited values for each of the keys in the first:

Assuming I had two strings, one acts a string of space delimited keys, and another acts as the associative space-delimited values for each of the keys in the first:

KEYS="key_1 key_2 key_3"
VALS="value1 aDifferentValue_2 theFinalValue"

So in this case, key_1 in $KEYS has an associated value of value1, key_2 had an associated value of aDifferentValue_2, and so on. Say the key in question was key_2, stored in variable $FIELD, what is the easiest way using sed and/or awk to find out that based off its word position, the value must be aDiffere开发者_运维百科ntValue_2? Is there a way to create an associative array using the substrings of $KEYS as the keys and $VALS as the values?


Since this is tagged bash, you can have actual associative arrays (bash v4):

KEYS="key_1 key_2 key_3"
VALS="value1 aDifferentValue_2 theFinalValue"
keys=( $KEYS )
vals=( $VALS )
declare -A map
for (( i=0; i<${#keys[@]}; i++ )); do
  map["${keys[$i]}"]="${vals[$i]}"
done
for idx in "${!map[@]}"; do
  echo "$idx -> ${map[$idx]}"
done

outputs

key_1 -> value1
key_2 -> aDifferentValue_2
key_3 -> theFinalValue

If you don't have bash v4, you can still use the keys and vals indexed arrays and:

get_key_idx() {
  for (( i=0; i<${#keys[@]}; i++ )); do 
    if [[ "$key" = "${keys[$i]}" ]]; then
      echo $i
      return 0
    fi
  done
  return 1
}

key="key_2"
if idx=$(get_key_idx $key); then
  echo "$key -> ${vals[$idx]}"
else
  echo "no mapping for $key"
fi


how about:

Variables:

/home/sirch>KEYS="key_1 key_2 key_3"
/home/sirch>VALS="value1 aDifferentValue_2 theFinalValue"

Code:

/home/sirch>cat s.awk
BEGIN{
    split(keystring,key," ");
    split(valstring,temp," ")
    for(i=1;i<=length(key);i++)val[key[i]]=temp[i]
}
END{print val["key_2"]}

Output:

/home/sirch>awk -v "keystring=$KEYS" -v "valstring=$VALS" -v "query=key_02" -f s.awk f
aDifferentValue_2

At first the shell variables KEYS and VALS are set. Then they are exported to akw as strings. These strings are then split on spaces, the resulting arrays saved in "key" and "temp". temp ist used to create the vinal hashmap "val". Now you can query for val["key_1"] etc. to get the corresponding value from the VALS-shell string.

To run this from a shell script, simply save the awk script to a file. Then set the shell variable query to your query string and call the awk-script from within your shell script with this variable. You will have give a dummy file, here "f", as argument to awk, to make this work.

HTH Chris


Pure POSIX shell:

#!/bin/sh

# set -x

assoc() {
  keys="$1 "
  values="$2 "
  key=$3
  while [ -n "$keys" -a -n "$values" ]; do
    key_=${keys%% *}
    keys=${keys#* }
    value_=${values%% *}
    values=${values#* }
    if [ $key_ = $key ]; then
      echo $value_
      return 0
    fi
  done
  return 1
}

keys='a b c'
values='1 2 3'
key=b
echo Value for key $key is _`assoc "$keys" "$values" "$key"`_

keys='a b c'
values='1 2 3'
key=xxx
echo Value for key $key is _`assoc "$keys" "$values" "$key"`_

keys='a b c'
values='1 2 3 4 5'
key=c
echo Value for key $key is _`assoc "$keys" "$values" "$key"`_

keys='a b c'
values='1 2'
key=c
echo Value for key $key is _`assoc "$keys" "$values" "$key"`_

When run:

  bash# ./1.sh   
Value for key b is _2_
Value for key xxx is __
Value for key c is _3_
Value for key c is __


Here's a bash-only alternative:

#!/bin/bash -x

KEYS="key_1 key_2 key_3"
VALS="value1 aDifferentValue_2 theFinalValue"

assoc() {
    read fkey rkeys
    read fval rvals
    if [ "X$fkey" == "X$1" ] ; then echo $fval; exit 0; fi
    if [ "X$rkeys" == "X" ] ; then exit 1; fi
    echo -e "$rkeys\n$rvals" | assoc $1
}

echo -e "$KEYS\n$VALS" | assoc key_2


another AWK solution (maybe shorter?)

echo "$keys $vals"|awk '{t=NF/2;for(i=1;i<=t;i++)a[$i]=$(i+t);}{print a[KeyYouWANT]}'

test:

kent$  keys="key_1 key_2 key_3"    
kent$  vals="value1 aDifferentValue_2 theFinalValue"
kent$  echo "$keys $vals"|awk '{t=NF/2;for(i=1;i<=t;i++)a[$i]=$(i+t);} END{print a["key_2"]}'  

aDifferentValue_2

You could also put the keyStr you want in a variable

kent$  want=key_3
kent$  echo "$keys $vals"|awk -v x=$want '{t=NF/2;for(i=1;i<=t;i++)a[$i]=$(i+t);} END{print a[x]}'   

theFinalValue

or you want to see a full list:

kent$  echo "$keys $vals"|awk '{t=NF/2;for(i=1;i<=t;i++)a[$i]=$(i+t);} END{for(x in a)print x,a[x]}'

key_1 value1
key_2 aDifferentValue_2
key_3 theFinalValue


Here's a sed solution:

keys="key_1 key_2 key_3" values="value_1 value_2 value_3" key="key_2" # variables used

str="$keys @@$values $key " # keys/values delimit by '@@' add key N.B. space after key!

sed -rn ':a;s/^(\S* )(.*@@)(\S* )(.*)/\2\4\1\3/;ta;s/^@@(\S* ).*\1(\S*).*/\2/p' <<<"$str"

# re-arrange keys/values into a lookup table then match on key using back reference
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号