Skip to content

TUI with whiptail

Whiptail allows shell scripts to display dialog boxes. It aims to show information, or to get input from the user in a friendly way. Whiptail is included by default on Debian.

whiptail is a dialog replacement using newt instead of ncurses. whiptail is designed to be drop-in compatible with dialog, but has fewer features: some dialog boxes are not implemented, such as tailbox, timebox, calendarbox, etc.

Note that there are also other dialog-compatible programs like xdialog (Displays X11 Windows) and zenity (aka gdialog, displays Gtk Windows).

info_box.sh
#!/bin/bash
TERM=ansi whiptail --title "Example Dialog" --infobox "This is an example of an info box." 8 60
# --title is displayed at the top of the dialog box.
# 1st argument to --infobox is the dialog box text which is shown beneath the title.
# next two arguments specify the height and width of the dialog box.
# There is a bug that makes the Info Box not display on some shells.
# If this is the case you can set the terminal emulation to something different and it will work.
# TERM=ansi whiptail # ...

Important notes:

  • --title value is displayed at the top of the dialog box.
  • 1st argument to --infobox is the dialog box text which is shown beneath the title.
  • Next two arguments specify the height and width of the dialog box.
message_box.sh
#!/bin/bash
whiptail --title "Example Dialog" --msgbox "This is an example of a message box. You must hit OK to continue." 8 60
# the last two arguments specify the height and width of the message box.
# press Enter to exit the message box

Last two arguments specify the height and width of the message box.

Press Enter keyboard key to exit the message box.

yes_no_box.sh
#!/bin/bash
whiptail --title "Example Dialog" --yesno "This is an example of a yes/no box." 8 60
# the last two arguments specify the height and width of the message box.
# the return statement of the previous command is stored into $? variable
# if the reply is Yes, then $? = 0
# if the reply is No, then $? = 1
if [ $? -eq 0 ]; then
# you can do whatever you want within this if statement, in this case a message box is displayed
whiptail --title "Yes Dialog" --msgbox "User replied YES. You must hit OK to continue." 8 60
else
whiptail --title "No Dialog" --msgbox "User replied NO. You must hit OK to continue." 8 60
fi

Important notes:

  • return statement of the previous command, in bash, is stored into $? variable
  • if the reply is Yes, then $? is equal to 0
  • if the reply is No, then $? is equal to 1
input_box.sh
#!/bin/bash
COLOR=$(whiptail --inputbox "What is your favorite Color?" 8 40 "Blue" --title "Color Input Dialog" 3>&1 1>&2 2>&3)
# trick to swap stdout and stderr.
# you can pack this inside if, but it seems really long for some 80-column terminal users.
# save the return value into a variable
EXIT_STATUS=$?
if [ $EXIT_STATUS = 0 ]; then
whiptail --title "Color Dialog" --msgbox "User selected Ok and entered color = $COLOR" 8 60
else
whiptail --title "Color Dialog" --msgbox "User selected Cancel" 8 60
fi
echo "# Exit status = $EXIT_STATUS"

Please Note: 3>&1 1>&2 2>&3 trick creates a temporary file descriptor (called 3) to swap 1 (standard output) and 2 (standard error), allowing you to capture the output into a variable.

text_box.sh
#!/bin/bash
echo "Welcome to Bash $BASH_VERSION" > test_textbox
# arguments: filename height width
whiptail --textbox test_textbox 12 80
# A text box with contents of the given file inside.
# Add --scrolltext if the filename is longer than the window.
echo "APT list installed packages:\n"> long_textbox
apt list | grep "installed" | head -n 20 | cut -d'/' -f1 >> long_textbox
whiptail --textbox --scrolltext long_textbox 12 80
# shows the first 20 APT packages installed on the machine
# press the right key to move the "<Ok>" button and press Enter to exit the text-box
rm test_textbox long_textbox

Press the right key to move the <Ok> button and press Enter to exit the text-box.

password_box.sh
#!/bin/bash
PASSWORD=$(whiptail --passwordbox "Please enter your secret password" 8 60 --title "password dialog" 3>&1 1>&2 2>&3)
# trick to swap stdout and stderr.
# you can pack this inside if, but it seems really long for some 80-column terminal users.
EXIT_STATUS=$?
if [ $EXIT_STATUS == 0 ]; then
whiptail --title "Password Dialog" --msgbox "User selected Ok and entered password = $PASSWORD" 8 60
else
whiptail --title "Password Dialog" --msgbox "User selected Cancel" 8 60
fi
echo "# Exit status = $EXIT_STATUS"
menu.sh
#!/bin/bash
# utility tag names for menu and switch-case
TAG_1="(Option 1)"
TAG_2="(Option 2)"
TAG_3="(Option 3)"
TAG_4="(Option 4)"
# menu should be used when you want the user to select one option from a list, such as for navigating a program.
MENU_CHOOSE=$(whiptail --title "Menu Dialog" --menu "Choose an option" 16 60 8 \
"$TAG_1" "Describe option 1." \
"$TAG_2" "Describe option 2." \
"$TAG_3" "Describe option 3." \
"$TAG_4" "Describe option 4." \
3>&1 1>&2 2>&3)
# values given to --menu are:
# 1. text describing the menu ("Choose an option")
# 2. height of the dialog (16)
# 3. width of the dialog (60)
# 4. height of the menu list (8)
# The rest of the values are a list of menu options in the format `tag item`.
# `tag` is the name of the option which is printed to `stderr` when selected, and `item` is the description of the menu option.
# you can then use a switch-case to perform different action based on the menu result
EXIT_STATUS=$?
if [ $EXIT_STATUS == 0 ]; then
MSG_BOX=
case $MENU_CHOOSE in
"$TAG_1")
MSG_BOX="User selected Ok and entered option 1."
;;
"$TAG_2")
MSG_BOX="User selected Ok and entered option 2."
;;
"$TAG_3")
MSG_BOX="User selected Ok and entered option 3."
;;
"$TAG_4")
MSG_BOX="User selected Ok and entered option 4."
;;
*)
MSG_BOX="Some error occurs."
;;
esac
whiptail --title "Menu Dialog" --msgbox "$MSG_BOX" 8 60
else
whiptail --title "Menu Dialog" --msgbox "User selected Cancel" 8 60
fi

Values given to --menu are:

  1. text describing the menu ("Choose an option")
  2. height of the dialog (16)
  3. width of the dialog (60)
  4. height of the menu list (8)

The rest of the values are a list of menu options in the format tag item.

tag is the name of the option which is printed to stderr when selected, and item is the description of the menu option.

If you are presenting a very long menu and want to make best use of the available screen, you can calculate the best box size using this trick:

#!/bin/bash
eval_lines() {
echo $(stty size | cut -d ' ' -f1)
}
eval_columns() {
echo $(stty size | cut -d ' ' -f2)
}
show_menu() {
local lines=$(eval_lines)
local columns=$(eval_columns)
whiptail --title "Menu example" --menu "Choose an option" $(( $lines / 2 )) $(( $columns / 2 )) $(( $lines / 2 - 8 )) # ...
}
# $lines and $columns variables represents the terminal lines and columns
check_list.sh
#!/bin/bash
# utility tag names for checklist and switch-case
TAG_1="(Option 1)"
TAG_2="(Option 2)"
TAG_3="(Option 3)"
TAG_4="(Option 4)"
# check list allows a user to select one or more options from a list.
CHECKLIST_CHOOSE=$(whiptail --title "Checklist Dialog" --checklist "Choose one or more options" 16 60 8 \
"$TAG_1" "Describe option 1." ON \
"$TAG_2" "Describe option 2." 1 \
"$TAG_3" "Describe option 3." 0 \
"$TAG_4" "Describe option 4." ON \
3>&1 1>&2 2>&3)
# press Space-bar to select/unselect an item
# press Enter to confirm the selection
# press Esc to Cancel the selection
# values given to --checklist are:
# 1. text describing the check list ("Choose one or more options")
# 2. height of the dialog (16)
# 3. width of the dialog (60)
# 4. height of the menu list (8)
# The rest of the values are a list of check list options in the format `tag item default_mode`.
# `default_mode` can be `ON` or `1` for the item to be marked as selected by default.
# `default_mode` can be `OFF` or `0` for the item to be marked as unselected by default.
# when the user confirms their selections, a list of `tag` representing the choices is printed to `stderr`.
# ---
# you can then use a switch-case to perform different action based on the check list result
EXIT_STATUS=$?
# echo "# EXIT_STATUS = $EXIT_STATUS"
# echo "# CHECKLIST_CHOOSE = $CHECKLIST_CHOOSE"
# using regex to print all the user selection
if [ $EXIT_STATUS == 0 ]; then
MSG_BOX="User selected Ok and entered: "
# check for each tag individually using regex [[ =~ ]]
if [[ $CHECKLIST_CHOOSE =~ "$TAG_1" ]]; then
MSG_BOX="$MSG_BOX\n- Option 1"
fi
if [[ $CHECKLIST_CHOOSE =~ "$TAG_2" ]]; then
MSG_BOX="$MSG_BOX\n- Option 2"
fi
if [[ $CHECKLIST_CHOOSE =~ "$TAG_3" ]]; then
MSG_BOX="$MSG_BOX\n- Option 3"
fi
if [[ $CHECKLIST_CHOOSE =~ "$TAG_4" ]]; then
MSG_BOX="$MSG_BOX\n- Option 4"
fi
# handle case where nothing was selected but OK was pressed
if [ -z "$CHECKLIST_CHOOSE" ]; then
MSG_BOX="User selected Ok but chose no options."
fi
whiptail --title "Checklist Dialog" --msgbox "$MSG_BOX" 16 60
else
whiptail --title "Checklist Dialog" --msgbox "User selected Cancel" 8 60
fi

Important notes:

  • press Spacebar to select/unselect an item
  • press Enter to confirm the selection
  • press Esc to Cancel the selection

Values given to --checklist are:

  1. text describing the menu ("Choose one or more options")
  2. height of the dialog (16)
  3. width of the dialog (60)
  4. height of the menu list (8)
  5. the rest of the values are a list of check list options in the format tag item default_mode

default_mode can be ON or 1 for the item to be marked as selected by default.
default_mode can be OFF or 0 for the item to be marked as unselected by default.

When the user confirms their selections, a list of tag representing the choices is printed to stderr.

radio_list.sh
#!/bin/bash
# utility tag names for radio list and switch-case
TAG_1="(Option 1)"
TAG_2="(Option 2)"
TAG_3="(Option 3)"
TAG_4="(Option 4)"
# radio list is a dialog where the user can select one option from a list.
# the difference between a radio list and a menu is that the user selects an option (using the space bar in whiptail) and then confirms that choice by hitting OK.
RADIOLIST_CHOOSE=$(whiptail --title "Radio List Dialog" --radiolist "Choose an option" 16 60 8 \
"$TAG_1" "Describe option 1." 1 \
"$TAG_2" "Describe option 2." 0 \
"$TAG_3" "Describe option 3." 0 \
"$TAG_4" "Describe option 4." 0 \
3>&1 1>&2 2>&3)
# press Space-bar to select/unselect an item
# press Enter to confirm the selection
# press Esc to Cancel the selection
# values given to --radiolist are:
# 1. text describing the radio list ("Choose an option")
# 2. height of the dialog (16)
# 3. width of the dialog (60)
# 4. height of the radio list (8)
# The rest of the values are a list of radio list options in the format `tag item`.
# `tag` is the name of the option which is printed to `stderr` when selected, and `item` is the description of the radio list option.
# if you are presenting a very long radio list and want to make best use of the available screen, you can calculate the best box size by.
# eval `resize`
# whiptail --title "Menu example" --radiolist "Choose an option" $LINES $COLUMNS $(( $LINES - 8 )) ...
# ---
# you can then use a switch-case to perform different action based on the radio list result
EXIT_STATUS=$?
if [ $EXIT_STATUS == 0 ]; then
MSG_BOX=
case $RADIOLIST_CHOOSE in
"$TAG_1")
MSG_BOX="User selected Ok and entered option 1."
;;
"$TAG_2")
MSG_BOX="User selected Ok and entered option 2."
;;
"$TAG_3")
MSG_BOX="User selected Ok and entered option 3."
;;
"$TAG_4")
MSG_BOX="User selected Ok and entered option 4."
;;
*)
MSG_BOX="Some error occurs."
;;
esac
whiptail --title "Radio List Dialog" --msgbox "$MSG_BOX" 8 60
else
whiptail --title "Radio List Dialog" --msgbox "User selected Cancel" 8 60
fi

Values given to --checklist are:

  1. text describing the menu ("Choose one or more options")
  2. height of the dialog (16)
  3. width of the dialog (60)
  4. height of the menu list (8)
  5. the rest of the values are a list of radio list options in the format tag item.

tag is the name of the option which is printed to stderr when selected, and item is the description of the radio list option.

progress_gauge.sh
#!/bin/bash
# syntax:
# whiptail --gauge <text> <height> <width> [<percent>]
# also, gauge can read `percent` from `stdin` (standard input)
function echo_progression() {
for ((i = 0 ; i <= 100 ; i+=1)); do
sleep 0.1
echo $i
done
}
echo_progression | whiptail --gauge "Waiting 0.1 sec from 0 to 100..." 8 60 0