Skip to content

Bash sample code: Create an interactive menu

Jean-Michel Gigault edited this page Aug 11, 2015 · 1 revision

Display an interactive menu in your Bash scripts like 42FileChecker does.


Sample code

We are going to create a 3 items menu so that the user will be asked to type a number between 1 and 3. The idea is to read the sandard input from the keyboard with the function read, to check if an action has been defined in an array called MY_ACTIONS and finally to execute the action with the command eval:

function do_libft
{
    echo "-> You selected LIBFT"
    display_menu
}

function do_libftasm
{
    echo "-> You selected LIBFTASM"
    display_menu
}

function do_exit
{
    echo "-> Exit the script"
    exit
}

# Function that displays the menu
function display_menu
{
    # Declare an array containing the actions to do
    local -a MY_ACTIONS
    MY_ACTIONS[1]="do_libft"
    MY_ACTIONS[2]="do_libftasm"
    MY_ACTIONS[3]="do_exit"

    # Display items
    local -i I=1
    echo "#########################"
    while [ $I -le ${#MY_ACTIONS[@]} ]
    do
        echo "$I) ${MY_ACTIONS[$I]}"
        (( I += 1 ))
    done

    # Read keyboard silently, one character at a time, 
    # and save the value in $SELECTION
    local SELECTION
    read -s -n1 SELECTION

    # Convert the ascii value of $SELECTION into a numeric index
    local ORD_VALUE=$(LC_CTYPE=C printf '%d' "'$SELECTION")
    (( ORD_VALUE -= 48 ))

    # Check if the index is valid
    if [ $ORD_VALUE -gt 0 -a $ORD_VALUE -le ${#MY_ACTIONS[@]} ]
    then
        # If yes, evaluate the action and exit the function
        eval ${MY_ACTIONS[${ORD_VALUE}]}
        return
    fi

    # If not, call display_menu one more time
    echo "-> INVALID SELECTION"
    display_menu
}

# Call the menu
display_menu

Tips


Convert ASCII number into numeric value
function get_ord
{
    LC_CTYPE=C printf '%d' "'$1"
}

get_ord "1"                      # Print ASCII value of "1": 49
get_ord "A"                      # Print ASCII value of "A": 65

declare -i VALUE=$(get_ord "1")  # Save the result in a variable $VALUE
(( VALUE -= 48 ))                # Do an arithmetic operation
echo $VALUE                      # Print the numeric value of "1": 1

Check if an index is valid

In the current example of menu, we set an array MY_ARRAY containing 3 indexes from 1 to 3. The arithmetic comparisons we need to do are: "greater than" 0 (-gt) and "less or equal than" 3 (-le). 3 is also the count number of items in the array, then we use the special symbol # before the name of the array to get its length and the special parameter [@] to expand all items of the array. -a is an "AND" operator that permits to join our two comparisons.

# C equivalent: if (ord_value > 0 and ord_value <= length)
if [ $ORD_VALUE -gt 0 -a $ORD_VALUE -le ${#MY_ACTIONS[@]} ]
then
    # ...
fi

We also could have use the "zero" test (-z) that returns success when a string is empty:

# Invert the test by using the symbol '!'
if [ ! -z ${MY_ACTIONS[$ORD_VALUE]} ]
then
    # This test causes an error when $ORD_VALUE is a negative number
    # Array indexes can only be positive or zero
fi

Evaluate a string as a command line

eval is a builtin command that interprets a string or a list of arguments as a command line:

# Convert a string into a command line
declare ACTION="echo '42FileChecker' | cat -e"
eval $ACTION

# Convert a list of arguments into a command line
eval "echo" "42FileChecker" "|" "cat -e"