Bash - 如何将列表分配给数组成员

3
bash 中,我有一个包含值的数组:
declare -A my_array
my_array['key1']='value1'
my_array['key2']='value2'

echo "-----my_array-----"
echo key1: ${my_array['key1']}
echo key2: ${my_array['key2']}

哪些方面表现良好:

-----my_array----- 
key1: value1 
key2: value2

虽然我想要一个关键字对应一系列值的列表。 我尝试过以下方法:

my_array['key2']=('value2.1', 'value2.2')  

出现了以下错误:

my_array['key2']: 无法将列表分配给数组成员

如何使用语法将列表添加到数组元素中,并访问键的索引值?


2
bash 数组/列表是一维的。 - Ted Lyngmo
4
是时候使用一种具有实际数据结构支持的不同语言了。 - chepner
1
@TedLyngmo 大多数编程语言中的列表都是一维的;不同之处在于它们存储的值不像 bash 中那样被限制为字符串。 - chepner
这个回答解决了您的问题吗?Bash中的多维数组 - pjh
2个回答

2
不是。从技术上讲,shell变量只用于保存字符串。但是......

下创建数组的数组(甚至是关联数组)

我将使用磁盘空闲: df -k 的输出来创建以设备为索引的关联数组。由于tmpfs伪设备用于许多不同的行为,因此这个字段必须包含列表

使用特殊分隔符的最简单方法。

在这里,我使用空格作为分隔字段内容的分隔符,因为它们通过命令进行空格分割。然后我使用bell特殊字符(\ 007)来分隔子数组。

以下是一个示例,使用df的输出,创建按设备排序的列表。结果,所有的tmpfs都将被存储在一起:

declare -A dfVars
{
    read _;
    while read -r dev _ use free _ mpnt ;do
        dfVars["${dev##*/}"]+="$use $((use+free)) $mpnt"$'\07'
    done
} < <(
    LANG=C df -lk
)

你可以将其解析为:
for dev in ${!dfVars[@]} ;do
    IFS=$'\a\n' read -d '' -ra fields <<<"${dfVars["$dev"]%$'\a'}"
    for line in "${fields[@]}";do
        read -r use tot mpnt <<<"$line"
        printf "%-20s %11d %11d  %s\n" $dev $use $tot $mpnt
    done
done

可能会输出类似以下的内容:
tmpfs                          0      188928  /dev/shm
tmpfs                      19284      188928  /run
tmpfs                          4        5120  /run/lock
tmpfs                          0      188928  /sys/fs/cgroup
tmpfs                          0       37784  /run/user/33
tmpfs                          0       37784  /run/user/1001
devtmpfs                       0      184596  /dev
mmcblk0p1                  23193       42136  /boot
root                   115253500   117652696  /

一切都存储在$dfVars中:
declare -p dfVars

declare -A dfVars=([tmpfs]=$'0 188928 /dev/shm\a19284 188928 /run\a4 5120 /run/l
ock\a0 188928 /sys/fs/cgroup\a0 37784 /run/user/33\a0 37784 /run/user/1001\a' [d
evtmpfs]=$'0 184596 /dev\a' [mmcblk0p1]=$'23193 42136 /boot\a' [root]=$'11529034
8 117652696 /\a' )

Stronger: 使用 nameref 索引数组:

使用这种方法,您可以创建独立变量,可以是数组、关联数组甚至字符串...

uuvar() { printf -v "$1" %s%09d "$2" $((++uuVarCnt));}
declare -A dfVars
{
     read _
     while read -r dev _ use free _ mpnt ;do
         uuvar vname _df_
         dfVars["${dev##*/}"]+=$vname' ' 
         read -a $vname <<<"$use $((use+free)) $mpnt"
         done
} < <(LANG=C df -lk)

将其解析为:
for dev in ${!dfVars[@]} ;do
    for sub in ${dfVars["$dev"]};do
        declare -n subDf=$sub
        printf "%-20s %11d %11d  %s\n" "$dev" "${subDf[@]}"
    done
done

tmpfs                          0      188928  /dev/shm
tmpfs                      19284      188928  /run
tmpfs                          4        5120  /run/lock
tmpfs                          0      188928  /sys/fs/cgroup
tmpfs                          0       37784  /run/user/33
tmpfs                          0       37784  /run/user/1001
devtmpfs                       0      184596  /dev
mmcblk0p1                  23193       42136  /boot
root                   115254628   117652696  /

“$dfVars”和“子变量”看起来像什么:
declare -p dfVars uuVarCnt;set | grep ^_df_

declare -A dfVars=([tmpfs]="_df_000000003 _df_000000004 _df_000000005 _df_000000
006 _df_000000008 _df_000000009 " [devtmpfs]="_df_000000002 " [mmcblk0p1]="_df_0
00000007 " [root]="_df_000000001 " )
declare -- uuVarCnt="9"
_df_000000001=([0]="115335328" [1]="117652696" [2]="/")
_df_000000002=([0]="0" [1]="184596" [2]="/dev")
_df_000000003=([0]="0" [1]="188928" [2]="/dev/shm")
_df_000000004=([0]="19284" [1]="188928" [2]="/run")
_df_000000005=([0]="4" [1]="5120" [2]="/run/lock")
_df_000000006=([0]="0" [1]="188928" [2]="/sys/fs/cgroup")
_df_000000007=([0]="23193" [1]="42136" [2]="/boot")
_df_000000008=([0]="0" [1]="37784" [2]="/run/user/33")
_df_000000009=([0]="0" [1]="37784" [2]="/run/user/1001")

相同,但使用关联数组:
declare -A dfVars
uuvar() { printf -v "$1" %s%09d "$2" $((++uuVarCnt));}
{
    read _
    while read -r dev _ use free _ mpnt ;do
        uuvar vname _df_
        dfVars["${dev##*/}"]+=$vname' '
        declare -A $vname
        printf -v $vname[used] %d $use
        printf -v $vname[total] %d $((use+free))
        printf -v $vname[mount\ point] %s "$mpnt"
    done
} < <(LANG=C df -lk)
for dev in ${!dfVars[@]} ;do
    for sub in ${dfVars["$dev"]} ;do
        declare -n subDf=$sub
        printf "%-20s %11d %11d  %s\n" "$dev" \
            "${subDf[used]}" "${subDf[total]}" "${subDf["mount point"]}"
    done
done

## < snip same output >

那么

declare -p dfVars uuVarCnt;set | grep ^_df_

declare -A dfVars=([tmpfs]="_df_000000003 _df_000000004 _df_000000005 _df_000000
006 _df_000000008 _df_000000009 " [devtmpfs]="_df_000000002 " [mmcblk0p1]="_df_0
00000007 " [root]="_df_000000001 " )
declare -- uuVarCnt="9"
_df_000000001=([used]="115358360" [total]="117652696" ["mount point"]="/" )
_df_000000002=([used]="0" [total]="184596" ["mount point"]="/dev" )
_df_000000003=([used]="0" [total]="188928" ["mount point"]="/dev/shm" )
_df_000000004=([used]="19284" [total]="188928" ["mount point"]="/run" )
_df_000000005=([used]="4" [total]="5120" ["mount point"]="/run/lock" )
_df_000000006=([used]="0" [total]="188928" ["mount point"]="/sys/fs/cgroup" )
_df_000000007=([used]="23193" [total]="42136" ["mount point"]="/boot" )
_df_000000008=([used]="0" [total]="37784" ["mount point"]="/run/user/33" )
_df_000000009=([used]="0" [total]="37784" ["mount point"]="/run/user/1001" )

1
这是一个不错的解决方法 :D - Alg_D

1

虽然不能直接实现,但可以通过变量扩展的方式来实现,如下所示:

declare -A my_array
my_array['key1']='value1'
my_array['key2']='value2'

value1="true value1"
value2="true value2"

检查:

$ echo ${!my_array['key1']}
true value1

$ echo ${!my_array['key2']}
true value2

如果value*是一个数组,那么情况会更加复杂,但也是可能的:

...
value1=("true value1.1" "true value1.2")
value1=("true value2.1" "true value2.2")
index="${my_array['key1']}[0]"

$ echo "${!index}"
true value2.1

这段代码可以封装在一个函数中:
fun(){
    local index1=${!1}
    local index2="$index1[$2]"
    echo  "${!index2}"
}

$ fun "my_array['key2']" 1
true value2.2

但是在我看来,你应该接受这个限制并重新思考设计,或者考虑使用另一种语言。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接