When creating interactive scripts, it is commonly needed to provide a way for users to select an option from a menu. It can be accomplished with the read command in a loop. But there is a smarter way.

#!/usr/bin/env bash

PS3="What is your favorite programming language? "

select language in Bash Java C# Python; do
  echo "You choose $language"
done

Try it out!

The PS3 variable is used to set the prompt for the select statement.

It keeps prompting forever. We can break out with the break keyword.

#!/usr/bin/env bash

PS3="What is your favorite programming language? "

select language in Bash Java C# Python; do
  echo "You choose $language"
  break
done

If you want different actions to take place depending on the selected option then you can combine it with a case statement.

#!/usr/bin/env bash

PS3="What is your favorite programming language? "

select language in Bash Java C# Python; do
  case $language in
  "Bash")
    echo "$language and crash :(){ :|:& };:"
    ;;
  "Java")
    echo "You like strong coffe, because you are all about that $language GUI"
    ;;
  "C#")
    echo "You wear glasses so you can $language"
    ;;
  "Python")
    echo "$language because you can't keep anything private"
    ;;
  esac
done

Or if you only want to prompt once.

#!/usr/bin/env bash

PS3="What is your favorite programming language? "

select language in Bash Java C# Python quit; do
  break
done

case $language in
"Bash")
  echo "$language and crash :(){ :|:& };:"
  ;;
"Java")
  echo "You like strong coffe, because you are all about that $language GUI"
  ;;
"C#")
  echo "You wear glasses so you can $language"
  ;;
"Python")
  echo "$language because you can't keep anything private"
  ;;
"quit")
  echo "Done."
  break
  ;;
esac

You can an array for options.

#!/usr/bin/env bash

options=(Bash Java C# Python)

select option in "${options[@]}"; do
  echo "You selected $option"
done

And you can use the output from a command.

#!/usr/bin/env bash

PS3="Please select a folder in your home directory: "

IFS=$'\n'
options=($(ls -1 ~))

select option in "${options[@]}"; do
  echo "You selected $option"
done

It uses -1 flag for ls command to output one item per line.

By default bash split items on any whitespace character (space, tab, newline). It can be changed by setting the IFS (Internal Field Separator) variable. We are setting it to newline, because we got one item per line.