Bash:将两个字符串直接拆分成关联数组

Bash: Split two strings directly into associative array

我有两个由相同数量的子字符串除以分隔符的字符串。

我需要从子字符串创建键值对。

简短示例: 输入:

firstString='00011010:00011101:00100001'
secondString='H:K:O'
delimiter=':'

想要的结果:

${translateMap['00011010']} -> 'H'
${translateMap['00011101']} -> 'K'
${translateMap['00100001']} -> 'O'

所以,我写道:

IFS="$delimiter" read -ra fromArray <<< "$firstString"
IFS="$delimiter" read -ra toArray <<< "$secondString"
declare -A translateMap

curIndex=0
for from in "${fromArray[@]}"; do
    translateMap["$from"]="${toArray[curIndex]}"
    ((curIndex++))
done

有没有什么方法可以直接从 2 个字符串创建关联数组而不需要不需要的数组和循环?类似于:

IFS="$delimiter" read -rA translateMap["$(read -ra <<< "$firstString")"] <<< "$secondString"

可能吗?

这可能不是您所期望的,但这有效:

key_string="A:B:C:D"
val_string="1:2:3:4"
declare -A map
while [ -n "$key_string" ] && [ -n "$val_string" ]; do
    IFS=: read -r key key_string <<<"$key_string"
    IFS=: read -r val val_string <<<"$val_string"
    map[$key]="$val"
done

for key in "${!map[@]}"; do echo "$key => ${map[$key]}"; done

它在read函数中使用递归来重新分配字符串值。 这种方法的缺点是它会破坏原始字符串。 while 循环不断检查两个字符串的长度是否为非零。

除了上面的纯 bash,您可以使用任何命令来生成关联数组。参见

这通常看起来像:

declare -A map="( $( magic_command ) )"

其中 magic_command 生成类似

的输出
[key1]=val1
[key2]=val2
[key3]=val3

在这种情况下我们使用命令:

paste -d "" <(echo "[${key_string//:/]=$'\n'[}]=") \
            <(echo "${val_string//:/$'\n'}")

我们使用 bash substitution to replace the delimiter with a newline 的地方。但是,任何其他 magic_command 都可以。完成:

key_string="A:B:C:D"
val_string="1:2:3:4"
declare -A map="( $(paste -d "" <(echo "[${key_string//:/]=$'\n'[}]=") \
                                <(echo "${val_string//:/$'\n'}"))      )"
for key in "${!map[@]}"; do echo "$key => ${map[$key]}"; done

两个示例都生成以下输出

D => 4
C => 3
B => 2
A => 1

不完全是你所问的答案,但至少它更短:

key='00011010:00011101:00100001'
value='H:K:O'
ifs=':'

IFS="$ifs" read -ra keys <<< "$key"
IFS="$ifs" read -ra values <<< "$value"
declare -A kv

for ((i=0; i<${#keys[*]}; i++)); do
    kv[${keys[i]}]=${values[i]}
done

作为旁注,您可以一步初始化关联数组:

declare -A kv=([key1]=value1 [key2]=value2 [keyn]=valuen)

但我不知道如何在你的情况下使用它。

@accdias 通过 declare -A 命令分配值的答案的一个(有点令人费解的)变体,但每个步骤都需要一些解释...

首先,我们需要将每个项目的 2 个变量分成单独的行:

$ echo "${firstString}" | tr "${delimiter}" '\n'
00011010
00011101
00100001

$ echo "${secondString}" | tr "${delimiter}" '\n'
H
K
O

这样做的好处是我们现在可以将这 2 组 key/value 对作为单独的文件进行处理。

注意:对于本次讨论的其余部分,我将用 ':' 替换 "${delimiter}" 以使其稍微(但不多)不那么复杂。

接下来我们使用paste命令将我们的2个'files'合并为一个文件;我们还将指定 ']' 作为 key/value 映射之间的分隔符:

$ paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n')
00011010]H
00011101]K
00100001]O

我们现在 运行 这些结果通过几个 sed 模式来构建我们的数组分配:

$ paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n') | sed 's/^/[/g;s/]/]=/g'
[00011010]=H
[00011101]=K
[00100001]=O

我们现在想做的是在 typeset -A 命令中使用这个输出,但不幸的是我们需要构建整个命令然后 eval 它:

$ evalstring="typeset -A kv=( "$(paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n') | sed 's/^/[/g;s/]/]=/g')" )"

$ echo "$evalstring"
typeset -A kv=( [00011010]=H
[00011101]=K
[00100001]=O )

如果我们想删除回车 returns 并放在一行中,我们在 sed 命令的输出中附加另一个 tr

$ evalstring="typeset -A kv=( "$(paste -d ']' <(echo "${firstString}" | tr ':' '\n') <(echo "${secondString}" | tr ':' '\n') | sed 's/^/[/g;s/]/]=/g' | tr '\n' ' ')" )"

$ cat "${evalstring}"
typeset -A kv=( [00011010]=H [00011101]=K [00100001]=O )

此时我们可以eval我们自动生成的typeset -A命令:

$ eval "${evalstring}"

现在遍历显示 key/value 对的数组:

$ for i in ${!kv[@]}; do echo "kv[${i}] = ${kv[${i}]}"; done
kv[00011010] = H
kv[00100001] = O
kv[00011101] = K

嘿,我确实说过这会有点令人费解! :-)

如果字符串中的值不使用空格,我建议采用这种方法

firstString='00011010:00011101:00100001'
secondString='H:K:O'
delimiter=':'

declare -A translateMap

firstArray=( ${firstString//$delimiter/' '} )
secondArray=( ${secondString//$delimiter/' '} )

for i in ${!firstArray[@]}; {
    translateMap[firstArray[$i]}]=${secondArray[$i]}
}