Classic Shell Scripting - Arnold Robbins [206]
then
TERM=$term
echo TERM is $TERM
export TERM
break
else
echo 'invalid.'
fi
done
When you run this code, you see this menu:
1) gl35a
2) t2000
3) s531
4) vt99
terminal?
The built-in shell variable PS3 contains the prompt string that select uses; its default value is the not particularly useful "#? ". For this reason, the first line of the preceding code sets it to a more relevant value.
The select statement constructs the menu from the list of choices. If the user enters a valid number (from 1 to 4), then the variable term is set to the corresponding value; otherwise, it is null. (If the user just presses Enter, the shell prints the menu again.)
The code in the loop body checks if term is non-null. If so, it assigns $term to the environment variable TERM, exports TERM, and prints a confirmation message; then the break statement exits the select loop. If term is null, the code prints an error message and repeats the prompt (but not the menu).
The break statement is the usual way of exiting a select loop. (A user can also type Ctrl-D—for end-of-input—to get out of a select loop. This gives the interactive user a uniform way of exiting, but it doesn't help the shell programmer much.)
We can refine our solution by making the menu more user friendly so that the user doesn't have to know the terminfo name of the terminal. We do this by using quoted character strings as menu items, and then using case to determine the terminfo name. The new version is shown in Example 14-2.
Example 14-2. Combining select with more user-friendly menu items
echo 'Select your terminal type:'
PS3='terminal? '
select term in \
'Givalt GL35a' \
'Tsoris T-2000' \
'Shande 531' \
'Vey VT99'
do
case $REPLY in
1) TERM=gl35a ;;
2) TERM=t2000 ;;
3) TERM=s531 ;;
4) TERM=vt99 ;;
*) echo 'invalid.' ;;
esac
if [[ -n $term ]]; then
echo TERM is $TERM
export TERM
break
fi
done
This code looks a bit more like a menu routine in a conventional program, though select still provides the shortcut of converting the menu choices into numbers. We list each of the menu choices on its own line for reasons of readability, but we need continuation characters to keep the shell from complaining about syntax.
Here is what the user sees when this code is run:
Select your terminal type:
1) Givalt GL35a
2) Tsoris T-2000
3) Shande 531
4) Vey VT99
terminal?
This is a bit more informative than the previous code's output.
When the body of the select loop is entered, $term equals one of the four strings (or is null if the user made an invalid choice), whereas the built-in variable REPLY contains the number that the user selected. We need a case statement to assign the correct value to TERM; we use the value of REPLY as the case selector.
Once the case statement is finished, the if checks to see if a valid choice was made, as in the previous solution. If the choice was valid, then TERM has already been assigned, so the code just prints a confirmation message, exports TERM, and exits the select loop. If it wasn't valid, the select loop repeats the prompt and goes through the process again.
Within a select loop, if REPLY is set to the null string, the shell reprints the menu. This happens, as mentioned, when the user hits Enter. However, you may also explicitly set REPLY to the null string to force the shell to reprint the menu.
The variable TMOUT (time out) can affect the select statement. Before the select loop, set it to some number of seconds n, and if nothing is entered within that amount of time, the select will exit.
Extended Test Facility
ksh introduced the extended test facility, delineated by [[ and ]]. These are shell keywords, special to the syntax of the shell, and not a command. Recent versions of bash have adopted this special facility as well.
[[...]] differs from the regular test and [...] commands in that word expansion and pattern expansion (wildcarding) are not done. This means that quoting is much less necessary. In effect, the contents of [[...]] form a separate sublanguage, which makes it easier