【问题标题】:Use an array to dynamically assign a variable name, then extract array data while avoiding eval使用数组动态分配变量名,然后在避免eval的同时提取数组数据
【发布时间】:2018-05-09 09:26:37
【问题描述】:

我正在重写一个主要的脚本。其中一部分是出于通常可能避免 eval 的原因而删除 eval。我在寻找管理以下场景类型的可行方法时遇到了麻烦。

考虑这两个 eval 语句:

eval echo '${'#${role}'[@]}' users
loc_NagiosUsersAll+=($( eval echo '${'${role}'[@]}' " " ))

第一个打印以筛选给定角色内的用户数量。第二个将所有这些用户添加到一个更大的数组中。

角色是被评估的当前角色恰好是任何角色。我们称它为只读。我们可以将第一个语句写成如下:

printf "${#read_only[@]} users"

我已经尝试了几十种括号、引号和各种杂技的组合来放弃 eval 并让它们发挥作用。

这里是 echo echo 版本(使用实际角色之一)进行比较:

$ echo echo '${'#${role}'[@]}' users
echo ${#authorized_for_all_host_commands[@]} users
$ echo ${#authorized_for_all_host_commands[@]} users
6 users
$ eval echo '${'#${role}'[@]}' users
6 users

我已经设法放弃了所有其他 eval 语句,但这种类型就像一个勾号一样被挖掘出来。

那么,我怎样才能比使用 eval 更安全?

更多代码...

declare -a NagiosUserRolesAll=( authorized_for_read_only 
                            authorized_for_all_services 
                            authorized_for_all_hosts 
                            authorized_for_system_information 
                            authorized_for_configuration_information 
                            authorized_for_system_commands 
                            authorized_for_all_service_commands 
                            authorized_for_all_host_commands ) 

function func_NagiosUserDataGet(){ # was load_data_tables 
    local -a loc_NagiosUsersAll="" 
    printf "Loading users into the different tables.  \n" 
    for role in "${NagiosUserRolesAll[@]}" 
        do 
            declare -ag $role="($( cat ${svnFilePath} | sed -n "s/${role}=//p" | sed  's/,/ /g' ))"
            declare -n ref="${role}" # copy the reference, not the contents of the array 
            printf "The role ${role} has ${#ref[@]} users.  \n" 
            loc_NagiosUsersAll+=(${ref[@]}) 
            loc_NagiosUsersAll+=" " 
        done 
    printf "Creating list of unique users.  \n" 
    NagiosUsersAllClean=($( echo ${loc_NagiosUsersAll[@]} | tr ' ' '\n' | 
sort -u )) 
    printf "Total users:  ${#NagiosUsersAllClean[@]}.  \n" 
} 

function func_NagiosUsersShow(){ # was show_all_users 
    if [[ "${svnFileExists}" == '1' ]] ; then 
        printf "You'll need to checkout a cgi.cfg file first.  \n" 
        return 1 
    fi 
    printf "\nThese are the roles with their users.  \n\n"
    for role in "${NagiosUserRolesAll[@]}" 
        do 
            # declare -ng ref="${role}" # copy the reference, not the contents of the array 
            printf "These users are in ${const_TextRed}${role}" 
            printf "${const_TextPlain}:  " 
            printf "${const_TextGreen}" 
            # printf "${ref[@]}  \n" # FAILS 
            printf "${ref[*]}  \n" # ALSO FAILS (prints one user for each role)
            # eval echo '${'${role}'[@]}' # WORKS 
            printf "${const_TextPlain}  \n" 
        done 
    printf "\nNow for a list of unique users.  \n\n"
    func_EnterToContinue
    printf "Unique users list:  \n" 
    for i in "${!NagiosUsersAllClean[@]}" 
        do 
            printf "$i:  ${NagiosUsersAllClean[$i]}  \n" 
        done 
    func_EnterToContinue
} 

【问题讨论】:

  • 如果你的 bash 版本是 4.3 或更高版本,你可以说类似declare -n role="read_only"
  • 这将如何帮助解决此声明的问题?评估 echo '${'#${role}'[@]}' 用户
  • 我已经发布了下面的示例代码。

标签: arrays bash command eval substitution


【解决方案1】:

使用 bash 4.3 或更高版本,您可以通过说 declare -n varref 将变量声明为对另一个变量的引用。这是一个示例代码:

#!/bin/bash

declare -a read_only=(Alice Bob Charlie)
declare -a authorized_for_all_host_commands=(Dan Emma Fred George Harry Irene)
declare -a loc_NagiosUsersAll

declare -n role="read_only"
echo ${#role[@]} users
# yields "3 users"
loc_NagiosUsersAll+=(${role[@]})

declare -n role="authorized_for_all_host_commands"
echo ${#role[@]} users
# yields "6 users"
loc_NagiosUsersAll+=(${role[@]})

echo ${#loc_NagiosUsersAll[@]} users
# yields "9 users"
echo ${loc_NagiosUsersAll[@]}
# yields "Alice Bob Charlie Dan Emma Fred George Harry Irene"

希望这会有所帮助。

[已编辑] 以下代码是根据您的最新帖子修改的版本。

declare -a NagiosUserRolesAll=( authorized_for_read_only
                            authorized_for_all_services
                            authorized_for_all_hosts
                            authorized_for_system_information
                            authorized_for_configuration_information
                            authorized_for_system_commands
                            authorized_for_all_service_commands
                            authorized_for_all_host_commands )

function func_NagiosUserDataGet(){ # was load_data_tables
    local -a loc_CaptureUsersPerRole=""
    local -a loc_NagiosUsersAll=""
    printf "Loading users into the different tables.  \n"
    for role in "${NagiosUserRolesAll[@]}"; do
        declare -a $role="($( cat ${svnFilePath} | sed -n "s/${role}=//p" | sed  's/,/ /g' ))"
        printf "These users have the role ${role}:  "

        declare -n ref=$role         # copy the reference, not the contents of the array
        printf "${#ref[@]} users  \n"

        loc_NagiosUsersAll+=(${ref[@]})
#       loc_NagiosUsersAll+=" "
    done
    printf "Creating list of unique users.  \n"
    NagiosUsersAllClean=($( echo ${loc_NagiosUsersAll[@]} | tr ' ' '\n' | sort -u ))
    printf "Total users:  ${#NagiosUsersAllClean[@]}.  \n"
}

[5 月 12 日编辑] 关键是对引用的赋值应该出现在declare -n 语法中。否则会产生意想不到的结果。示例如下:

declare -a arys=(ary_a ary_b ary_c)
declare -a ary_a=(a1 a2 a3)
declare -a ary_b=(b1 b2 b3)
declare -a ary_c=(c1 c2 c3)

# test 1
for role in "${arys[@]}"; do
    declare -n ref="$role"
    echo "${ref[@]}"
done
# => works properly

# test 2
for role in "${arys[@]}"; do
    declare -n ref
    ref="$role"
    echo "${ref[@]}"
done
# => does not work correctly

[5 月 15 日编辑] 这是修改后的版本应该可以工作:

declare -a NagiosUserRolesAll=( authorized_for_read_only
                            authorized_for_all_services
                            authorized_for_all_hosts
                            authorized_for_system_information
                            authorized_for_configuration_information
                            authorized_for_system_commands
                            authorized_for_all_service_commands
                            authorized_for_all_host_commands )

function func_NagiosUserDataGet(){ # was load_data_tables
    local -a loc_NagiosUsersAll=""
    printf "Loading users into the different tables.  \n"
    for role in "${NagiosUserRolesAll[@]}"
        do
            declare -ag $role="($( cat ${svnFilePath} | sed -n "s/${role}=//p" | sed  's/,/ /g' ))"
            declare -n ref="${role}" # copy the reference, not the contents of the array
            printf "The role ${role} has ${#ref[@]} users.  \n"
            loc_NagiosUsersAll+=(${ref[@]})
            loc_NagiosUsersAll+=" "
        done
    printf "Creating list of unique users.  \n"
    NagiosUsersAllClean=($( echo ${loc_NagiosUsersAll[@]} | tr ' ' '\n' |
sort -u ))
    printf "Total users:  ${#NagiosUsersAllClean[@]}.  \n"
}

function func_NagiosUsersShow(){ # was show_all_users
    if [[ "${svnFileExists}" == '1' ]] ; then
        printf "You'll need to checkout a cgi.cfg file first.  \n"
        return 1
    fi
    printf "\nThese are the roles with their users.  \n\n"
    for role in "${NagiosUserRolesAll[@]}"
        do
            declare -ng ref="${role}" # copy the reference, not the contents of the array
            printf "These users are in ${const_TextRed}${role}"
            printf "${const_TextPlain}:  "
            printf "${const_TextGreen}"
            # printf "${ref[@]}  \n" # FAILS
            printf "${ref[*]}  \n" # => should work
            # eval echo '${'${role}'[@]}' # WORKS
            printf "${const_TextPlain}  \n"
        done
    printf "\nNow for a list of unique users.  \n\n"
    func_EnterToContinue
    printf "Unique users list:  \n"
    for i in "${!NagiosUsersAllClean[@]}"
        do
            printf "$i:  ${NagiosUsersAllClean[$i]}  \n"
        done
    func_EnterToContinue
}

【讨论】:

  • 这里的麻烦是$role也是一个数组。我尝试了您的主题的变体,但只能获得人们可能期望的一些效果(并且您上面的代码肯定会为一个简单的变量做这些)。我在我的问题中添加了一些代码供您阅读。
  • 恐怕您正在混合数组名称、数组内容和对数组的引用。尝试我发布的修改版本。据我所知,它看起来很有效。
  • 我们非常接近。此功能通过一些调整可以正常工作,但还有另一个功能(用于向用户显示)这个eval echo '${'${role}'[@]}' 可以工作,但这个printf "${ref[@]} \n" 不能(它只是为每个角色提供第一个用户)。我今晚已经结束了,但周日晚上会再去一次,让你知道。谢谢!
  • @JamesIsIn 很高兴知道我们走在正确的轨道上。关于您当前的问题,我已经更新了我的答案,提到了一个可能的原因,尽管没有看到您的代码我无法具体说明。
  • 好的,我更新了我的代码以反映当前版本的 get 函数并添加了 show 函数。我已经用 FAILS 和 WORKS 标记了 show 中的两行。再次感谢。
【解决方案2】:

这两个函数的最终工作版本如下。我不清楚为什么 printf 行需要这种精确的格式,但是你有它。

function func_NagiosUserDataGet(){ # was load_data_tables 
    local -a loc_NagiosUsersAll="" 
    printf '%s\n' "Loading users into the different tables.  " 
    for role in "${NagiosUserRolesAll[@]}" 
        do 
            declare -ag "${role}"="($( cat ${svnFilePath} | sed -n "s/${role}=//p" | sed  's/,/ /g' ))" 
            declare -n ref="${role}" # copy the reference, not the contents of the array 
            printf "The role ${role} has ${#ref[@]} users.  %s\n" 
            loc_NagiosUsersAll+=("${ref[@]}") 
            loc_NagiosUsersAll+=(" ") 
        done 
    printf '%s\n' "Creating list of unique users.  " 
    NagiosUsersAllClean=($( echo "${loc_NagiosUsersAll[@]}" | tr ' ' '\n' | sort -u )) 
    printf "There are ${#NagiosUsersAllClean[@]} total users.  %s\n" 
} 

function func_NagiosUsersShow(){ # was show_all_users 
    if [[ "${svnFileExists}" == '1' ]] ; then 
        printf '%s\n' "You'll need to checkout a cgi.cfg file first.  " 
        return 1 
    fi 
    printf '%s\n' "" "These are the roles with their users.  " ""
    for role in "${NagiosUserRolesAll[@]}" 
        do 
            declare -n ref="${role}" # copy the reference, not the contents of the array 
            printf "The role ${const_TextRed}${role}${const_TextPlain} has these ${#ref[@]} users:  %s" 
            printf "${const_TextGreen} %s\n" 
            printf '%s ' "${ref[@]} " 
            printf "${const_TextPlain} %s\n" 
            printf "%s\n" 
        done 
    read -p "Now, would you like to see a list of unique users?  (y/N)  " 
    if  func_YesOrNo "${REPLY}" 
                then 
                    printf '%s\n' "Unique users list:  " 
                    for i in "${!NagiosUsersAllClean[@]}" 
                        do 
                            printf "$i:  ${NagiosUsersAllClean[$i]}  %s\n" 
                        done 
                    func_EnterToContinue 
                else 
                    return 0  
            fi 
} 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-28
    • 1970-01-01
    • 2014-08-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-11
    • 1970-01-01
    • 2011-01-05
    相关资源
    最近更新 更多