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]}
}
我有两个由相同数量的子字符串除以分隔符的字符串。
我需要从子字符串创建键值对。
简短示例: 输入:
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]}
}