在bash 4.1下使用具有变量名的关联数组

在bash 4.1下使用具有变量名的关联数组,bash,associative-array,Bash,Associative Array,我试图在bash-4.1 $cat hostname_abc.txt host_type type_foo SoftA version123 SoftB version456 要获得一个输出,您可以看到一个版本的Soft[a,B]被使用了多少次,请按主机类型分组: $./list_versions.sh [type_foo] 11 times SoftA: [version123] 1 times [version444] 5 times

我试图在bash-4.1

$cat hostname_abc.txt
host_type type_foo
SoftA version123
SoftB version456
要获得一个输出,您可以看到一个版本的Soft[a,B]被使用了多少次,请按主机类型分组:

$./list_versions.sh
[type_foo] 11 times
    SoftA:
        [version123] 1 times
        [version444] 5 times
        [version567] 5 times
    SoftB:
        [version456] 9 times
        [version777] 2 times
[type_bar] 6 times
    SoftA:
        [version444] 6 times
    SoftB:
        [version111] 4 times
        [version777] 2 times
我事先不知道主机类型和版本的列表。 因此,我尝试将每个主机类型的计数保存在关联数组中,并根据模板主机类型软[A,B]动态创建关联数组的名称,这些数组存储每个主机类型软[A,B]的每个版本的计数

我多次尝试使用不同的语法和间接形式,因此我根据我的目标重新制作了以下更具可读性的脚本:

#!/usr/bin/env bash

# ----- generated test conditions -----
echo -e "host_type typeA\nSoftA v2\nSoftB v1" > hostname_1.txt
echo -e "host_type typeB\nSoftA v1\nSoftB v1" > hostname_2.txt
echo -e "host_type typeB\nSoftA v1\nSoftB v0" > hostname_3.txt
echo -e "host_type typeA\nSoftA v0\nSoftB v0" > hostname_4.txt
echo -e "host_type typeA\nSoftA v3\nSoftB v2" > hostname_5.txt
echo -e "host_type typeB\nSoftA v3\nSoftB v1" > hostname_6.txt
echo -e "host_type typeB\nSoftA v2\nSoftB v2" > hostname_7.txt
echo -e "host_type typeA\nSoftA v1\nSoftB v2" > hostname_8.txt
echo -e "host_type typeC\nSoftA v0\nSoftB v4" > hostname_9.txt
list_hostname() {
    for i in {1..9}; do
        echo "hostname_${i}.txt"
    done
}

declare -A list_host_type

while read f; do

    #parse the hostname files
    while read l; do
        [[ $l = *"host_type"* ]] && host_type="$( echo $l | cut -d' ' -f2)"
        [[ $l = *"SoftA"* ]] && versionA="$( echo $l | cut -d' ' -f2)"
        [[ $l = *"SoftB"* ]] && versionB="$( echo $l | cut -d' ' -f2)"
    done < <( cat "$f" )

    #count the number of hosts by host_type
    [[ ${list_host_type[$host_type]} ]] && ((list_host_type[$host_type]++)) || list_host_type[$host_type]='1'

    #create associative arrays with a name only know at runtime
    declare -A "${host_type}_SoftA"
    declare -A "${host_type}_SoftB"

    #count the number of host for the couple host_type and Soft[A,B], stored on the dynamically named assiociative array
    [[ ${${host_type}_SoftA[$versionA]} ]] && ((${host_type}_SoftA[$versionA]++)) || ${host_type}_SoftA[$versionA]='1'
    [[ ${${host_type}_SoftB[$versionB]} ]] && ((${host_type}_SoftB[$versionB]++)) || ${host_type}_SoftB[$versionB]='1'
done < <( list_hostname )

#print a non pretty-formated output
echo '==== result ====='
for m in "${!list_host_type[@]}"; do
    echo "host type: $m  count: ${list_model[$m]}"
    for versionA in "${!${m}_softA[@]}"; do
        echo "    SoftA  version: $versionA  count: ${${m}_SoftA[$versionA]}"
    done
    for versionB in "${!${m}_softB[@]}"; do
        echo "    SoftB  version: $versionB  count: ${${m}_SoftB[$versionB]}"
    done
done
#/usr/bin/env bash
#----生成的测试条件-----
echo-e“主机类型A\nSoftA v2\nSoftB v1”>hostname\u 1.txt
echo-e“主机类型类型B\nSoftA v1\nSoftB v1”>hostname_2.txt
echo-e“主机类型类型B\nSoftA v1\nSoftB v0”>hostname\u 3.txt
echo-e“主机类型A\nSoftA v0\nSoftB v0”>主机名4.txt
echo-e“主机类型A\nSoftA v3\nSoftB v2”>hostname_5.txt
echo-e“主机类型类型B\nSoftA v3\nSoftB v1”>hostname_6.txt
echo-e“主机类型类型B\nSoftA v2\nSoftB v2”>hostname\u 7.txt
echo-e“主机类型A\nSoftA v1\nSoftB v2”>hostname\u 8.txt
echo-e“主机类型C\nSoftA v0\nSoftB v4”>hostname\u 9.txt
列出主机名(){
因为{1..9}中的i;do
echo“hostname_${i}.txt”
完成
}
声明-列表\主机\类型
同时读取f;做
#解析主机名文件
读l时;做
[[$l=*“主机类型”*]&主机类型=“$(echo$l | cut-d'-f2)”
[[$l=*“SoftA”*]&&versionA=“$(echo$l | cut-d'-f2)”
[[$l=*“SoftB”*]&&versionB=“$(echo$l | cut-d'-f2)”

完成<我认为在Bash中不能对数组使用动态变量名。 (我尝试了一些方法,但无法理解语法。) 即使可能,我想这也很难理解


一种可能的解决方法是使用单个关联数组, 使用“复合键”。 即,例如,使用主机类型、软件和版本的逗号分隔值:

while read f; do
    line=0
    while read col1 col2; do
        if [[ $line = 0 ]]; then
            host_type=$col2
        else
            soft=$col1
            version=$col2
            index=$host_type,$soft,$version
            ((list_host_type[$index]++))
        fi
        ((line++))
    done < <( cat "$f" )
done < <( list_hostname )

for m in "${!list_host_type[@]}"; do
    echo $m = ${list_host_type[$m]}
done
然后使用这个关联数组来计算所需的统计信息。下面是一个粗略的示例实现:

get_host_types() {
    local names=(${!list_host_type[@]})
    printf "%s\n" "${names[@]%%,*}" | sort -u
}

get_soft() {
    local host_type=$1
    local names=(${!list_host_type[@]})
    for name in "${names[@]}"; do
        [[ ${name%%,*} = $host_type ]] && echo $name
    done | cut -d, -f2 | sort -u
}

get_versions() {
    local prefix=$1
    local names=(${!list_host_type[@]})
    for name in "${names[@]}"; do
        [[ ${name%,*} = $prefix ]] && echo $name
    done | cut -d, -f3 | sort -u
}

indent="    "
for host_type in $(get_host_types); do
    echo "[$host_type]"
    for soft in $(get_soft $host_type); do
        echo "$indent$soft:"
        for version in $(get_versions $host_type,$soft); do
            index=$host_type,$soft,$version
            echo "$indent$indent[$version] ${list_host_type[$index]} times"
        done
    done
done
作为产出生产:


总而言之,最好使用适当的编程语言来实现这一点。

您的脚本在Bash 4.4中不起作用。你知道吗?(您是否要求我们帮助您使其工作?
get_host_types() {
    local names=(${!list_host_type[@]})
    printf "%s\n" "${names[@]%%,*}" | sort -u
}

get_soft() {
    local host_type=$1
    local names=(${!list_host_type[@]})
    for name in "${names[@]}"; do
        [[ ${name%%,*} = $host_type ]] && echo $name
    done | cut -d, -f2 | sort -u
}

get_versions() {
    local prefix=$1
    local names=(${!list_host_type[@]})
    for name in "${names[@]}"; do
        [[ ${name%,*} = $prefix ]] && echo $name
    done | cut -d, -f3 | sort -u
}

indent="    "
for host_type in $(get_host_types); do
    echo "[$host_type]"
    for soft in $(get_soft $host_type); do
        echo "$indent$soft:"
        for version in $(get_versions $host_type,$soft); do
            index=$host_type,$soft,$version
            echo "$indent$indent[$version] ${list_host_type[$index]} times"
        done
    done
done
[typeA]
    SoftA:
        [v0] 1 times
        [v1] 1 times
        [v2] 1 times
        [v3] 1 times
    SoftB:
        [v0] 1 times
        [v1] 1 times
        [v2] 2 times
[typeB]
    SoftA:
        [v1] 2 times
        [v2] 1 times
        [v3] 1 times
    SoftB:
        [v0] 1 times
        [v1] 2 times
        [v2] 1 times
[typeC]
    SoftA:
        [v0] 1 times
    SoftB:
        [v4] 1 times