select - select is like a cross between a while loop and a for loop that displays its list as a menu and then repeats a read until one of the action-cmds issues a break.
Structure :
select var in list do action-cmds done
When executed, select will display each string in list as a menu element and number each element sequentially. It will then provide the PS3 prompt and wait for input. If a string is quoted, its contents will be treated as a single menu element.
The user selects the desired menu choice by entering the number to the left of the list element. The element selected is copied to the variable specified immediately after the select keyword.
Keep in mind the whole menu item is copied, so if your menu item was "My favorite year" all three words will be copied to the variable. So, keep your menu items short and sweet.
Example :
query() { select ans in nope "try again" quit "give up" do if [ "$ans" = quit ] then break fi if [ "$ans" = "give up" ] then break fi echo "You picked $ans" done }
Once user enters a choice and presses [enter], the selection may be processed in the action body. If there is no break out of the loop, the input prompt will be displayed again, even if the choice entered is invalid. However, if the user enters no value at the prompt, then the menu will be re-displayed.
The variable set by select keeps the assigned string after the execution breaks out of the select loop. So, you may use select to get valid input from the user and process the choice outside the loop.
exec - We have looked at exec in the context of forking or replacing the current command interpreter with a command. But shells can exec to open new file descriptors.
Recall that we have the three standard file descriptors, 0 - standard input, 1 - standard output, and 2 - standard error. With exec we can create 7 additional file descriptors (3-9) for special use in shell scripts and functions.
General syntax :
exec m > output-file
exec n < input-file
where exec is the command, n and m are a digit in the range of 3-9 and input-file and output-files are actual files.
The trick to using file descriptors is the & which we have already used when combine the output and error file descriptors together. This is called file descriptor duplication. (See page 470 of the Sobell book).
After establishing a file descriptor filename pair, use redirection and file descriptor duplication to establish the connection.
cmd >& m
will send standard output to the file descriptor and thus the file specified.
To break a connection between the descriptor and the file, use :
exec m>&-
Make sure you match the redirection direction to the initial pairing.
Use file desriptors when your script or function needs to read input from different sources or redirect output differently at different times.
Exercise : #Try the following two versions of gather, one at a time.
gather () { local ans echo -e "Input data :\c" read ans echo $ans } |
If we use command substitution and invoke "gather", assigning its output to a and press [enter]. Then echo out the variable.
value=`gather`
# enter something here and press [enter]
echo $value
Notice that both the prompt message created by gather and the echo of your input are assigned to the variable.
# Now re-enter "gather" like so:
gather () { local ans # open file descriptor 3 to /dev/tty (the terminal) exec 3> /dev/tty # redirect standard output to file descriptor 3 echo -e "Input data : \c" >&3 read ans # don't redirect the output of the value read from the user echo $ans # close the file descriptor exec 3>&- } |
Now invoke "gather" and assign its output to a variable. Then display the variable.
value=`gather`
echo $value
Notice that this time, the prompt was properly displayed and only the desired value was assigned to the variable.
"trap" - traps signals such as [ctrl] c.
General structure :
trap [[ "command sequence"] n ...]
where "trap" is the command. This is usually coded at the top of the function or shell script but may be coded elsewhere and may be repeated so different control characters are trapped in different parts of the script.
"command sequence" is an optional sequence of semi-colon delimited commands. If open and closed quotes stated with no commands inside, then trap causes trapped signals to be ignored.
So :
trap 2
in a shell script will prevent a [ctrl]c from interrupting the script.
If you do wish the script or function to terminate after taking special actions for a particular signal, include an exit or return in the command sequence. The command sequence must be quoted.
[[n]..] is a space delimited list of signals to be trapped. See the signal man page, section 7 to see signal definitions and which can be trapped.
If you use 0 or the reserved word EXIT for the signal, the command sequence will run on termination for any reason. Don't use it if you are coding a trap in a function.
For the exercise below, copy the code into a file and make it executable. You may name it what ever you want. For discussion, I will assume it is called "adder". Run it and test it out.
The 1st trap traps [ctrl]c signal and exits with a message and an error status.
The second displays the sum on termination, even if it because of the trapped [ctrl]c.
The third cause the HUP signal to be ignored. Quotes still required.
#!/bin/bash trap 'echo "You should use q"; exit 2' 2 trap 'echo "Your sum is $sum"' EXIT trap '' 1 sum=0 # endless loop, must use break to quit while true do echo -e "Enter a number or q to quit with total :\c" read ans if [ "$ans" = q ] then break fi # Add only if a number, may be negatively signed if test `echo $ans | grep "^-\{0,1\}[0-9][0-9]*$"` then sum=`expr $sum + $ans` fi done The easist way to test this is to log in to a second terminal. Run "adder" from one terminal. Try entering a few numbers and quit normally. Note the output. Next, restart "adder". From the other terminal run : ps -u $USER and find the process id for adder. Now, issue a kill command with the -HUP option on the proccess id assigned to "adder". Check back in 1st terminal. "adder" should still be running. Try the same again, except use -INT signal instead. "adder" should now terminate after printing current sum.
|