< Day Day Up > |
5.4. selectAll of the flow-control constructs we have seen so far are also available in the Bourne shell, and the C shell has equivalents with different syntax. Our next construct, select, is available only in the Korn shell and bash;[11] moreover, it has no analogy in conventional programming languages.
select allows you to generate simple menus easily. It has concise syntax, but it does quite a lot of work. The syntax is: select name [in list ] do statements that can use $name... done This is the same syntax as for except for the keyword select. And like for, you can omit the in list and it will default to "$@", i.e., the list of quoted command-line arguments. Here is what select does:
Here is a task that adds another command to our pushd and popd utilities. The display and selection of directories is best handled by using select. We can start off with something along the lines of:[12]
selectd ( ) { PS3='directory? ' select selection in $DIR_STACK; do if [ $selection ]; then #statements that manipulate the stack... break else echo 'invalid selection.' fi done } If you type DIR_STACK="/usr /home /bin" and execute this function, you'll see: 1) /usr 2) /home 3) /bin directory? The built-in shell variable PS3 contains the prompt string that select uses; its default value is the not particularly useful "#?". So the first line of the above 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 the number of directories), then the variable selection is set to the corresponding value; otherwise it is null. (If the user just presses RETURN, the shell prints the menu again.) The code in the loop body checks if selection is non-null. If so, it executes the statements we will add in a short while; then the break statement exits the select loop. If selection is null, the code prints an error message and repeats the menu and prompt. The break statement is the usual way of exiting a select loop. Actually (like its analog in Java and C), it can be used to exit any surrounding control structure we've seen so far (except case, where the double semicolons act like break) as well as the while and until we will see soon. We haven't introduced break until now because it is considered bad coding style to use it to exit a loop. However, it can make code easier to read if used judiciously. break is necessary for exiting select when the user makes a valid choice.[13]
Now we'll add the missing pieces to the code: selectd ( ) { PS3='directory? ' dirstack=" $DIR_STACK " select selection in $dirstack; do if [ $selection ]; then DIR_STACK="$selection${dirstack%% $selection *}" DIR_STACK="$DIR_STACK ${dirstack##* $selection }" DIR_STACK=${DIR_STACK% } cd $selection break else echo 'invalid selection.' fi done } The first two lines initialize environment variables. dirstack is a copy of DIR_STACK with spaces appended at the beginning and end so that each directory in the list is of the form space directory space. This form simplifies the code when we come to manipulating the directory stack. The select and if statements are the same as in our initial function. The new code inside the if uses bash's pattern-matching capability to manipulate the directory stack. The first statement sets DIR_STACK to selection, followed by dirstack with everything from selection to the end of the list removed. The second statement adds everything in the list from the directory following selection to the end of DIR_STACK. The next line removes the trailing space that was appended at the start. To complete the operation, a cd is performed to the new directory, followed by a break to exit the select code. As an example of the list manipulation performed in this function, consider a DIR_STACK set to /home /bin /usr2. In this case, dirstack would become /home /bin /usr2. Typing selectd would result in: $ selectd 1) /home 2) /bin 3) /usr2 directory? After selecting /bin from the list, the first statement inside the if section sets DIR_STACK to /bin followed by dirstack with everything from /bin onwards removed, i.e., /home. The second statement then takes DIR_STACK and appends everything in dirstack following /bin (i.e., /usr2) to it. The value of DIR_STACK becomes /bin /home /usr2. The trailing space is removed in the next line. |
< Day Day Up > |