bash does not recognize if conditions within a for loop for each element for an array of strings

Goal

I am using whiptail to let the user insert passwords to files which are used in conjunction with envsubst for some template files.

whiptail checklist

in order to render stuff in whiptail I use the following:

declare -A availableServices=(
    [grafana]="Grafana Dashboard"
    [influxdb]="InfluxDB v1.x TSDB"
    [node-red]="Node-RED Flow UI"
    [portainer]="Portainer Container Mgmt"
)

# Render TUI
function askGenerateCredential() {
    local message="Set Password for particular Servicen
                Press <SPACEBAR> to Select n
                Press <Enter> to Skip
                "
    local arglist=()
    
    # Generate a String for Whiptail Checkboxes
     # FORMAT: "<INDEX> <DESCRIPTION> <OFF>"
    for index in "${!availableServices[@]}";
    do
        # Default all Services are NOT-Selected (OFF)
        arglist+=("$index" "${availableServices[$index]}" "OFF")
    done
    SELECTED_SERVICES+=$($WHIPTAIL --title "Available Services within Populo" 
                --notags --separate-output 
                --ok-button Next 
                --nocancel 
                --checklist "$message" $LINES $COLUMNS $(( LINES - 12 )) 
                -- "${arglist[@]}" 
                3>&1 1>&2 2>&3)
}

the SELECTED_SERVICES is an array declared as

declare -a SELECTED_SERVICES=()

The rendering works well, however I have a specific if else condition in my script that is unable to execute specific commands based on the selected service in the array above.

askGenerateCredential
if [ -z "$SELECTED_SERVICES" ]; then
    echo "No Services were selected. Exiting..."
    exit 1
else
    for service in "${SELECTED_SERVICES}";
    do
        if [ "$service" == "influxdb" ]; then
            echo "Setting Credentials for InfluxDB"
            setInfluxDBCredentials
        elif [ "$service" == "grafana" ]; then
            echo "Setting Credentials for Grafana"
            setGrafanaCredentials
        elif [ "$service" == "node-red" ]; then
            echo "Setting Credentials for node-RED"
            setNodeRedCredentials
        elif [ "$service" == "portainer" ]; then
            echo "Setting Credential for Portainer"
            setPortainerCredentials
        fi
    done
fi

Upon adding the above block, my script exits with 0 code when I select multiple values via whiptail UI. On the contrary, when I only select one value in the UI, the respective function is called.

What am I doing wrong here? I wish that depending on the service variable, the respective function is called and not exit the script.

Code

#!/bin/env bash

WHIPTAIL=$(which whiptail)

if [ -z $WHIPTAIL ]
then
    echo "This script requires whiptail to render the TUI."
    exit 1
fi

declare -a SELECTED_SERVICES=()

LINES=$(tput lines)
COLUMNS=$(tput cols)


declare -A availableServices=(
    [grafana]="Grafana Dashboard"
    [influxdb]="InfluxDB v1.x TSDB"
    [node-red]="Node-RED Flow UI"
    [portainer]="Portainer Container Mgmt"
)

function askGenerateCredential() {
    local message="Set Password for particular Servicen
                Press <SPACEBAR> to Select n
                Press <Enter> to Skip
                "
    local arglist=()
    
    # Generate a String for Whiptail Checkboxes
     # FORMAT: "<INDEX> <DESCRIPTION> <OFF>"
    for index in "${!availableServices[@]}";
    do
        # Default all Services are NOT-Selected (OFF)
        arglist+=("$index" "${availableServices[$index]}" "OFF")
    done
    SELECTED_SERVICES+=$($WHIPTAIL --title "Available Services within Populo" 
                --notags --separate-output 
                --ok-button Next 
                --nocancel 
                --checklist "$message" $LINES $COLUMNS $(( LINES - 12 )) 
                -- "${arglist[@]}" 
                3>&1 1>&2 2>&3)
}

function setInfluxDBCredential() {
    echo "set admin password env var for influxdb"
}

function setGrafanaCredential() {
    echo "set admin password env var for grafana"
}

function setNodeRedCredential() {
    echo "set admin password env var for node-REd"
}

function setPortainerCredential() {
    echo "set password file for portainer"
}

askGenerateCredential
if [ -z "$SELECTED_SERVICES" ]; then
    echo "No Services were selected. Exiting..."
    exit 1
else
    for service in "${SELECTED_SERVICES}";
    do
        if [ "$service" == "influxdb" ]; then
            echo "Setting Credentials for InfluxDB"
            setInfluxDBCredentials
        elif [ "$service" == "grafana" ]; then
            echo "Setting Credentials for Grafana"
            setGrafanaCredentials
        elif [ "$service" == "node-red" ]; then
            echo "Setting Credentials for node-RED"
            setNodeRedCredentials
        elif [ "$service" == "portainer" ]; then
            echo "Setting Credential for Portainer"
            setPortainerCredentials
        fi
    done
fi

EDIT

Upon suggestion I tried ${SELECTED_SERVICES[@]} in the for loop and added an echo "$service" which is able to loop through the array however the if conditions are not triggered.

Asked By: Shan-Desai

||

I believe the logic in your askGenerateCredential() function is not doing what you want it to do. Currently the entire output of your whiptail command is being added to the array as a single element. If you want multiple elements split at newlines you should change it to something like this:

askGenerateCredential() {
    local message="Set Password for particular Servicen
                Press <SPACEBAR> to Select n
                Press <Enter> to Skip
                "
    local arglist=()
    
    # Generate a String for Whiptail Checkboxes
     # FORMAT: "<INDEX> <DESCRIPTION> <OFF>"
    for index in "${!availableServices[@]}";
    do
        # Default all Services are NOT-Selected (OFF)
        arglist+=("$index" "${availableServices[$index]}" "OFF")
    done
    readarray -t SELECTED_SERVICES < <("$WHIPTAIL" --title "Available Services within Populo" 
                --notags --separate-output 
                --ok-button Next 
                --nocancel 
                --checklist "$message" "$LINES" "$COLUMNS" $(( LINES - 12 )) 
                -- "${arglist[@]}" 
                3>&1 1>&2 2>&3)
}

See 4.2 Bash Builtin Commands


Also you are calling SELECTED_SERVICES as a variable instead of an array.

You need to change:

for service in "${SELECTED_SERVICES}"

to:

for service in "${SELECTED_SERVICES[@]}"

As is you will only expand the first element of the array. However as your code is written SELECTED_SERVICES will only ever receive a single element.

See 6.7 Arrays


Additionally case would probably be better used instead of the if/elif construct. Something like:

case $service in
    influxdb)   echo "Setting Credentials for InfluxDB"; setInfluxDBCredentials;;
    grafana)    echo "Setting Credentials for Grafana"; setGrafanaCredentials;;
    node-red)   echo "Setting Credentials for node-RED"; setNodeRedCredentials;;
    portainer)  echo "Setting Credential for Portainer"; setPortainerCredentials;;
    *)  echo "Error; unknown option: $service" >&2;;
esac
Answered By: jesse_b
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.