5.4 select

All 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.

[11] select is not available in bash versions prior to 1.14.

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    ]   
        statements that can use    $name...   

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:

·                 Generates a menu of each item in list , formatted with numbers for each choice

·                 Prompts the user for a number

·                 Stores the selected choice in the variable name and the selected number in the built-in variable REPLY

·                 Executes the statements in the body

·                 Repeats the process forever (but see below for how to exit)

Here is a task that adds another command to our pushd and popd utilities.

Task 5-5

Write a function that allows the user to select a directory from a list of directories currently in the pushd directory stack. The selected directory is moved to the front of the stack and it becomes the current working directory.

The display and selection of directories is best handled by using select . We can start off with something along the lines of: [12]

[12] Versions of bash prior to 1.14.3 have a serious bug with select . These versions will crash if the select list is empty. In this case, surround select s with a test for a null list.

 selectd () 
 PS3='directory? ' 
 select selection in $DIR_STACK; do 
 if [ $selection ]; then 
 #   statements that manipulate the stack...   
 echo 'invalid selection.' 

If you type DIR_STACK="/usr /home /bin" and execute this function, you'll see:

 1) /usr 
 2) /home 
 3) /bin 

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 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]

[13] A user can also type CTRL-D (for end-of-input) to get out of a select loop. This gives the user a uniform way of exiting, but it doesn't help the shell programmer much.

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 }" 
  cd $selection  
  echo 'invalid selection.'  

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  

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.


Learning the Bash Shell
Learning the bash Shell: Unix Shell Programming (In a Nutshell (OReilly))
ISBN: 0596009658
EAN: 2147483647
Year: 1998
Pages: 104

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: