Bash’s select keyword has been under my radar for very long time, I always keep an eye on it whenever I was reading a shell script, but I just couldn’t recall once that I had seen it being used in real script. Needless to say, I’ve never used it.

What it provides is a very simple user interface for getting an input from user, who choices one from a list. It’s very similar to case and loops. In fact, I would say it’s a combination of both. The syntax is:

$ help select
select: select NAME [in WORDS ... ;] do COMMANDS; done
    Select words from a list and execute commands.

    The WORDS are expanded, generating a list of words.  The
    set of expanded words is printed on the standard error, each
    preceded by a number.  If `in WORDS' is not present, `in "$@"'
    is assumed.  The PS3 prompt is then displayed and a line read
    from the standard input.  If the line consists of the number
    corresponding to one of the displayed words, then NAME is set
    to that word.  If the line is empty, WORDS and the prompt are
    redisplayed.  If EOF is read, the command completes.  Any other
    value read causes NAME to be set to null.  The line read is saved
    in the variable REPLY.  COMMANDS are executed after each selection
    until a break command is executed.

    Exit Status:
    Returns the status of the last command executed.

Quick example:

$ PS3='Which dir? ' ; select d in /*; do echo "$d"; done
1) /bin           4) /etc          7) /lib64       10) /opt         13) /run         16) /tmp
2) /boot          5) /home         8) /lost+found  11) /proc        14) /sbin        17) /usr
3) /dev           6) /lib          9) /mnt         12) /root        15) /sys         18) /var
Which dir?

Since it does act like loop, so the exit condition is important. You can add an additional option like 'Quit' or check if input is valid:

[[ -z $input ]] && break
[[ $input = Quit ]] && break

When user’s input is not a valid option, the variable is set to empty string. Note that no matter what the input is, $REPLY always gets the input. For example:

$ select input in a b c; do
>   echo "input = $input"
>   echo "REPLY = $REPLY"
> done
1) a
2) b
3) c
#? 1
input = a
REPLY = 1
#? invalid
input =
REPLY = invalid
#?