1

I am doing a major script re-write. Part of that is removing eval for the usual reasons one might avoid eval. I am running into trouble finding a viable way of managing the following scenario type.

Consider these two eval statements:

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

The first prints to screen the number of users within a given role. The second adds all of those users to a larger array.

Role is to be whatever the current role being assessed happens to be. Let's call it read_only. We could write that first statement as follows then:

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

I have tried dozens of combinations of parentheses, quotation marks, and various acrobatics to ditch eval and get those to work.

Here is the echo echo version (using one of the actual roles) for comparison:

$ 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

I've managed to ditch all the other eval statements but this type is dug in like a tick.

So, how can I do this more securely than using eval?

More code...

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
} 
3
  • If your bash version is 4.3 or later, you can say something like declare -n role="read_only". Commented May 9, 2018 at 12:58
  • How would that help solve the issue of this statement? eval echo '${'#${role}'[@]}' users Commented May 10, 2018 at 9:23
  • I've posted the example code below. Commented May 10, 2018 at 12:45

2 Answers 2

2

With bash 4.3 or later, you can declare a variable as a reference to another variable by saying declare -n varref. Here's an example code:

#!/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"

Hope this helps.

[Edited] The following code is the modified version based on your latest post.

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"
}

[Edited on May 12] The point is that the assignment to a reference should appear in the declare -n syntax. Otherwise it will yield an unexpected result. Here's the example:

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

[Edited on May 15] Here's the modified verion which should work:

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
}
Sign up to request clarification or add additional context in comments.

14 Comments

The trouble here is that $role is also an array. I tried variations on your theme but can only get access to some of the effects one might expect (and which your code above certainly does for a simple variable). I've added some code to my question for your perusal.
I'm afraid you are intermixing array name, array contents, and a reference to an array. Try the modified version which I've posted. It looks working so far as I can assume.
We are very close. This function is working correctly with some tweaking, but there is another function (for showing the users) where this eval echo '${'${role}'[@]}' works, but this printf "${ref[@]} \n" does not (it just gives the first user for each role). I'm done for the night but will have another go Sunday night and let you know. Thanks!
@JamesIsIn Good to know we are on the right track. Regarding your current issue, I've updated my answer with mentioning a possible cause, although I cannot be specific without seeing your code.
Ok, I've updated my code to reflect the current version of the get function and added the show function. I have marked the two lines in show with FAILS and WORKS. Thanks again.
|
0

The final working version of the two functions is as follows. I'm not clear why the printf lines needed that precise formatting, but there you have it.

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 
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.