Whiptail is a newt
-based utility allowing to build pseudo-graphical dialog boxes from shell scripts. Dialog uses ncurses
and is similar to whiptail
but has more options and, consequently, a bit harder to use. I find both useful when a user needs to be guided through a complex set of variables and to minimize fat-fingering.
The best to understand how to use dialog
and whiptail
(and sometimes you would want to use both of them in the same script) is by looking at some example. In this first example we’re prompting the user for database connection information to build the ${MYSQL}
connection string:
db_host=$(whiptail --inputbox "Database host:" 8 78 "localhost" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_port=$(whiptail --inputbox "Database port:" 8 78 "3336" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_user=$(whiptail --inputbox "Database user:" 8 78 "sqlman" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_pass=$(whiptail --passwordbox "Database pass:" 8 78 "dbs1721" --title "Query Dialog" 3>&1 1>&2 2>&3)
MYSQL="/usr/bin/mysql --batch --skip-column-names --max_allowed_packet=100M -h${db_host} --port=${db_port} -u${db_user} -p${db_pass} -e"
get_db_host() {
db_host=$(whiptail --inputbox "Database host:" 8 78 "localhost" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_port=$(whiptail --inputbox "Database port:" 8 78 "3336" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_user=$(whiptail --inputbox "Database user:" 8 78 "sqlman" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_pass=$(whiptail --passwordbox "Database pass:" 8 78 "dbs1721" --title "Query Dialog" 3>&1 1>&2 2>&3)
MYSQL="/usr/bin/mysql --batch --skip-column-names --max_allowed_packet=100M -h${db_host} --port=${db_port} -u${db_user} -p${db_pass} -e"
}
get_db_host() {
db_host=$(whiptail --inputbox "Database host:" 8 78 "localhost" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_port=$(whiptail --inputbox "Database port:" 8 78 "3336" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_user=$(whiptail --inputbox "Database user:" 8 78 "sqlman" --title "Query Dialog" 3>&1 1>&2 2>&3)
db_pass=$(whiptail --passwordbox "Database pass:" 8 78 "dbs1721" --title "Query Dialog" 3>&1 1>&2 2>&3)
MYSQL="/usr/bin/mysql --batch --skip-column-names --max_allowed_packet=100M -h${db_host} --port=${db_port} -u${db_user} -p${db_pass} -e"
}

The next example connects to the database to populate a whiptail
radio checklist with the names of available schemas. The second function connects to the selected database to get a list of tables and build another radio checklist. This is a good example of how to build dynamic menus.
db_name=$(whiptail --title "Available Databases" --radiolist "Selects a database:" 30 78 20 \
`$MYSQL "show databases;" | sed 's/$/ DB OFF/g'` 3>&1 1>&2 2>&3)
MYSQL="/usr/bin/mysql --batch --skip-column-names --max_allowed_packet=100M -h${db_host} --port=${db_port} -u${db_user} -p${db_pass} ${db_name} -e"
tbl_name=$(whiptail --title "Available Tables in ${db_name}" --radiolist "Selects a table:" 30 78 20 \
`$MYSQL "show tables;" | sed 's/$/ TBL OFF/g'` 3>&1 1>&2 2>&3)
get_db_name() {
db_name=$(whiptail --title "Available Databases" --radiolist "Selects a database:" 30 78 20 \
`$MYSQL "show databases;" | sed 's/$/ DB OFF/g'` 3>&1 1>&2 2>&3)
MYSQL="/usr/bin/mysql --batch --skip-column-names --max_allowed_packet=100M -h${db_host} --port=${db_port} -u${db_user} -p${db_pass} ${db_name} -e"
}
get_tbl_name() {
tbl_name=$(whiptail --title "Available Tables in ${db_name}" --radiolist "Selects a table:" 30 78 20 \
`$MYSQL "show tables;" | sed 's/$/ TBL OFF/g'` 3>&1 1>&2 2>&3)
}
get_db_name() {
db_name=$(whiptail --title "Available Databases" --radiolist "Selects a database:" 30 78 20 \
`$MYSQL "show databases;" | sed 's/$/ DB OFF/g'` 3>&1 1>&2 2>&3)
MYSQL="/usr/bin/mysql --batch --skip-column-names --max_allowed_packet=100M -h${db_host} --port=${db_port} -u${db_user} -p${db_pass} ${db_name} -e"
}
get_tbl_name() {
tbl_name=$(whiptail --title "Available Tables in ${db_name}" --radiolist "Selects a table:" 30 78 20 \
`$MYSQL "show tables;" | sed 's/$/ TBL OFF/g'` 3>&1 1>&2 2>&3)
}
The whiptail
utility has an unfortunate limitation: it can only ask you one question at a time. This can get tedious and distracting. The following example uses dialog
to present you with three questions and set variables var1
through var3
. The dialog
can’t assign each response to an individual variable. You would need to parse the output as illustrated below.
# Clear variables var1 - var3
# Launch dialog and gather user input
dialog_values="$(dialog --ok-label "Submit" \
--backtitle "Setting variables var1 - var7" \
--title "Some questions for you" \
"Enter var1:" 1 1 "${var1}" 1 16 25 0 \
"Enter var2:" 2 1 "${var2}" 2 16 25 0 \
"Enter var3:" 3 1 "${var3}" 3 16 25 0 \
# Now we parse the input assigned to colon-separated ${dialog_values} and set
eval "$(echo var${i})"="\"$(awk -F: -v i="${i}" '{print $i}' <<<${dialog_values})\""
# And this is just to show that the variables have been set
eval echo $(echo $`eval echo "var${i}"`)
dialog_do() {
# Clear variables var1 - var3
unset var{1..3}
# Set default values
var1="default value 1"
var2="default value 2"
var3="default value 3"
# Launch dialog and gather user input
exec 3>&1
dialog_values="$(dialog --ok-label "Submit" \
--clear \
--output-separator : \
--backtitle "Setting variables var1 - var7" \
--title "Some questions for you" \
--form "Answer this" \
20 60 0 \
"Enter var1:" 1 1 "${var1}" 1 16 25 0 \
"Enter var2:" 2 1 "${var2}" 2 16 25 0 \
"Enter var3:" 3 1 "${var3}" 3 16 25 0 \
2>&1 1>&3)"
exec 3>&-
clear
# Now we parse the input assigned to colon-separated ${dialog_values} and set
# individual variables
for i in {1..3}; do
eval "$(echo var${i})"="\"$(awk -F: -v i="${i}" '{print $i}' <<<${dialog_values})\""
done
# And this is just to show that the variables have been set
for i in {1..3}; do
eval echo $(echo $`eval echo "var${i}"`)
done
}
dialog_do() {
# Clear variables var1 - var3
unset var{1..3}
# Set default values
var1="default value 1"
var2="default value 2"
var3="default value 3"
# Launch dialog and gather user input
exec 3>&1
dialog_values="$(dialog --ok-label "Submit" \
--clear \
--output-separator : \
--backtitle "Setting variables var1 - var7" \
--title "Some questions for you" \
--form "Answer this" \
20 60 0 \
"Enter var1:" 1 1 "${var1}" 1 16 25 0 \
"Enter var2:" 2 1 "${var2}" 2 16 25 0 \
"Enter var3:" 3 1 "${var3}" 3 16 25 0 \
2>&1 1>&3)"
exec 3>&-
clear
# Now we parse the input assigned to colon-separated ${dialog_values} and set
# individual variables
for i in {1..3}; do
eval "$(echo var${i})"="\"$(awk -F: -v i="${i}" '{print $i}' <<<${dialog_values})\""
done
# And this is just to show that the variables have been set
for i in {1..3}; do
eval echo $(echo $`eval echo "var${i}"`)
done
}

And a few simple whiptail
dialog boxes that may come in handy in many scripts. This is a simple yes/no dialog:
if (whiptail --title "Continue/Exit" --yes-button "Continue" --no-button "Exit" --yesno "Proceed with the build?" 20 60); then
if (whiptail --title "Continue/Exit" --yes-button "Continue" --no-button "Exit" --yesno "Proceed with the build?" 20 60); then
echo "Continuing"
fi
if (whiptail --title "Continue/Exit" --yes-button "Continue" --no-button "Exit" --yesno "Proceed with the build?" 20 60); then
echo "Continuing"
fi

Here’s a simple message box. For some visual interest I added output of an array containing output of the ps
command:
IFS=$'\n'; a=($(ps -eLfw)); unset IFS
whiptail --title "Simple message box" --msgbox "This is first line\n\nThis is second line\n\n$(for ((i = 0; i < ${#a[@]}; i++)) ; do echo "${a[$i]}" ; done | grep httpd)" 30 100
IFS=$'\n'; a=($(ps -eLfw)); unset IFS
whiptail --title "Simple message box" --msgbox "This is first line\n\nThis is second line\n\n$(for ((i = 0; i < ${#a[@]}; i++)) ; do echo "${a[$i]}" ; done | grep httpd)" 30 100
IFS=$'\n'; a=($(ps -eLfw)); unset IFS
whiptail --title "Simple message box" --msgbox "This is first line\n\nThis is second line\n\n$(for ((i = 0; i < ${#a[@]}; i++)) ; do echo "${a[$i]}" ; done | grep httpd)" 30 100

This is another message box designed to hold a bit more content:
whiptail --title "Scrollbox" --scrolltext --msgbox "$(tail -1000 /var/log/messages)" 30 100
whiptail --title "Scrollbox" --scrolltext --msgbox "$(tail -1000 /var/log/messages)" 30 100
whiptail --title "Scrollbox" --scrolltext --msgbox "$(tail -1000 /var/log/messages)" 30 100

Thank you for this excellent article. That’s just what I wanted to know.
Chris