3

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 Service\n
                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 Service\n
                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.

1
  • set -x is always your friend in this class of issue (with BASH_XTRACEFD to set the log destination to somewhere else if you need to keep stderr clean). Commented Jun 7, 2022 at 1:34

1 Answer 1

5

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 Service\n
                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
2
  • @Shan-Desai: I don't think it actually does. See my edit.
    – jesse_b
    Commented Jun 6, 2022 at 14:41
  • 1
    @Shan-Desai: case can still be used, you would just add it within the for loop in place of the if/elif construct. The * probably wouldn't be necessary anymore although in this case likely wouldn't hurt
    – jesse_b
    Commented Jun 6, 2022 at 14:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .