bash - determine a substring in one variable when an associated substring is known in another variable
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
精彩评论