![]() | CONTENTS | ![]() |
When commands are executed from within a file, instead of from the command line, the file is called a shell script and the shell is running noninteractively. When the bash shell starts running noninteractively, it looks for the environment variable, BASH_ENV (ENV) and starts up the file (normally .bashrc) assigned as its value. After the BASH_ENV file has been read, the shell will start executing commands in the script.[1]
A shell script is normally written in an editor and consists of commands interspersed with comments. Comments are preceded by a pound sign and consist of text used to document what is going on.
The First Line. The first line at the top left corner of the script will indicate the program that will be executing the lines in the script. This line, commonly called the shbang line, is written as
#!/bin/bash
The #! is called a magic number and is used by the kernel to identify the program that should be interpreting the lines in the script. This line must be the top line of your script. The bash program can also accept arguments to modify its behavior. See Table 12.8 for a list of bash options.
Comments. Comments are lines preceded by a pound sign (#) and can be on a line by themselves or on a line following a script command. They are used to document your script. It is sometimes difficult to understand what the script is supposed to do if it is not commented. Although comments are important, they are often too sparse or not used at all. Try to get used to commenting what you are doing not only for someone else, but also for yourself. Two days from now you may not recall exactly what you were trying to do.
Executable Statements and bash Shell Constructs. A bash shell program consists of a combination of UNIX commands, bash shell commands, programming constructs, and comments.
Making the Script Executable. When you create a file, it is not given the execute permission. You need this permission to run your script. Use the chmod command to turn on the execute permission.
1 $ chmod +x myscript 2 $ ls -lF myscript -rwxr-xr-x 1 ellie 0 Jul 13:00 myscript*
EXPLANATION
|
A Scripting Session. In the following example, the user will create a script in the editor. After the user saves the file, the execute permissions are turned on, and the script is executed. If there are errors in the program, the shell will respond immediately.
(The Script) 1 #!/bin/bash 2 # This is the first Bash shell program of the day. # Scriptname: greetings # Written by: Barbara Bashful 3 echo "Hello $LOGNAME, it's nice talking to you." 4 echo "Your present working directory is 'pwd'." echo "You are working on a machine called 'uname -n'." echo "Here is a list of your files." 5 ls # List files in the present working directory 6 echo "Bye for now $LOGNAME. The time is 'date +%T'!" (The Command Line) $ greetings # Don't forget to turn turn on x permission! bash: ./greetings: Permission denied. $ chmod +x greetings $ greetings or ./greetings 3 Hello barbara, it's nice talking to you. 4 Your present working directory is /home/lion/barbara/prog You are working on a machine called lion. Here is a list of your files. 5 Afile cplus letter prac Answerbook cprog library prac1 bourne joke notes perl5 6 Bye for now barbara. The time is 18:05:07!
EXPLANATION
|
In the last chapter we talked about declaring and unsetting variables. Variables are set local to the current shell or as environment variables. Unless your shell script will invoke another script, variables are normally set as local variables within a script. (See "Variables".)
To extract the value from a variable, precede the variable with a dollar sign. You can enclose the variable within double quotes and the dollar sign will be interpreted by the shell for variable expansion. Variable expansion is not performed if the variable is enclosed in single quotes.
1 name="John Doe" or declare name="John Doe" # local variable 2 export NAME="John Doe" # global variable 3 echo "$name" "$NAME" # extract the value
The read command is a built-in command used to read input from the terminal or from a file (see Table 12.1). The read command takes a line of input until a newline is reached. The newline at the end of a line will be translated into a null byte when read. If no names are supplied, the line read is assigned to the special built-in variable, REPLY. You can also use the read command to cause a program to stop until the user hits Enter. To see how the read command is most effectively used for reading lines of input from a file, see "Looping Commands". The r option to read causes the backslash/newline pair to be ignored; the backslash is treated as part of the line. The read command has four options to control its behavior: a, e, p, and r.[2]
Format | Meaning |
---|---|
read answer | Reads a line from standard input and assigns it to the variable answer. |
read first last | Reads a line from standard input to the first whitespace or newline, putting the first word typed into the variable first and the rest of the line into the variable last. |
read | Reads a line from standard input and assigns it to the built-in variable, REPLY. |
read a arrayname | Reads a list of words into an array called arrayname.[a] |
read e | Used in interactive shells with command line editing in effect; e.g., if editor is vi, vi commands can be used on the input line.[a] |
read p prompt | Prints a prompt, waits for input, and stores input in REPLY variable.[a] |
read r line | Allows the input to contain a backslash.[a] |
[a] Not implemented on versions of bash prior to 2.0.
(The Script) #!/bin/bash # Scriptname: nosy echo -e "Are you happy? \c" 1 read answer echo "$answer is the right response." echo -e "What is your full name? \c" 2 read first middle last echo "Hello $first" echo n "Where do you work? " 3 read 4 echo I guess $REPLY keeps you busy! -----------------------------------------------------[a] 5 read -p "Enter your job title: " 6 echo "I thought you might be an $REPLY." 7 echo -n "Who are your best friends? " 8 read -a friends 9 echo "Say hi to ${friends[2]}." ------------------------------------------------------- (The Output) $ nosy Are you happy? Yes 1 Yes is the right response. 2 What is your full name? Jon Jake Jones Hello Jon 3 Where do you work? the Chico Nut Factory 4 I guess the Chico Nut Factory keeps you busy! 5 Enter your job title: Accountant 6 I thought you might be an Accountant. 7,8 Who are your best friends? Melvin Tim Ernesto 9 Say hi to Ernesto.
[a] The commands listed below this line are not implemented on versions of bash prior to 2.x.
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: printer_check # Script to clear a hung-up printer 1 if [ $LOGNAME != root ] then echo "Must have root privileges to run this program" exit 1 fi 2 cat << EOF Warning: All jobs in the printer queue will be removed. Please turn off the printer now. Press return when you are ready to continue. Otherwise press Control C. EOF 3 read JUNK # Wait until the user turns off the printer echo 4 /etc/rc.d/init.d/lpd stop # Stop the printer 5 echo -e "\nPlease turn the printer on now." 6 echo "Press Enter to continue" 7 read JUNK # Stall until the user turns the printer # back on echo # A blank line is printed 8 /etc/rc.d/init.d/lpd start # Start the printer
EXPLANATION
|
The declare Command. Variables can be declared as integers with the declare i command. If you attempt to assign any string value, bash assigns 0 to the variable. Arithmetic can be performed on variables that have been declared as integers. (If the variable has not been declared as an integer, the built-in let command allows arithmetic operations. See "The let Command" on page 732.) If you attempt to assign a floating point number, bash reports a syntax error. Numbers can also be represented in different bases such as binary, octal, and hex.
1 $ declare i num 2 $ num=hello $ echo $num 0 3 $ num=5 + 5 bash: +: command not found 4 $ num=5+5 $ echo $num 10 5 $ num=4*6 $ echo $num 24 6 $ num="4 * 6" $ echo $num 24 7 $ num=6.5 bash: num: 6.5: sytax error in expression (remainder of expression is ".5")
EXPLANATION
|
Listing Integers. The declare command with only the i argument will list all preset integers and their values, as shown in the following display.
$ declare i declare -ir EUID="15" # effective user id declare -ir PPID="235" # parent process id declare -ir UID="15" # user id
Representing and Using Different Bases. Numbers can be represented in decimal (base 10, the default), octal (base 8), hexadecimal (base 16), and a range from base 2 to 36.
FORMATvariable=base#number-in-that-base |
n=2#101 Base is 2; number 101 is in base 2
(The Command Line) 1 $ declare -i x=017 $ echo $x 15 2 $ x=2#101 $ echo $x 5 3 $ x=8#17 $ echo $x 15 4 $ x=16#b $ echo $x 11
EXPLANATION
|
The let Command. The let command is a bash shell built-in command that is used to perform integer arithmetic and numeric expression testing. To see what let operators your version of bash supports, type at the prompt:
help let
A list of the operators is also found in Table 12.4.
1 $ i=5 or let i=5 2 $ let i=i+1 $ echo $i 6 3 $ let "i = i + 2" $ echo $i 8 4 $ let "i+=1" $ echo $i 9 5 $ i=3 6 $ (( i+=4)) $ echo $i 7 7 $ (( i=i-2 )) $ echo $i 5
EXPLANATION
|
[a] Double parentheses (( )) are used to replace let on versions of bash 2.x.
Bash supports only integer arithmetic, but the bc, awk, and nawk utilities are useful if you need to perform more complex calculations.
(The Command Line) 1 $ n='echo "scale=3; 13 / 2" | bc' $ echo $n 6.500 2 product='gawk -v x=2.45 -v y=3.123 'BEGIN{printf "%.2f\n",x*y}'' $ echo $product 7.65
EXPLANATION
|
Information can be passed into a script via the command line. Each word (separated by whitespace) following the scriptname is called an argument.
Command line arguments can be referenced in scripts with positional parameters; for example, $1 for the first argument, $2 for the second argument, $3 for the third argument, and so on. After $9, curly braces are used to keep the number as one number. For example, positional parameter 10 is referenced as ${10}. The $# variable is used to test for the number of parameters, and $* is used to display all of them. Positional parameters can be set or reset with the set command. When the set command is used, any positional parameters previously set are cleared out. See Table 12.2.
Positional Parameter | What It References |
---|---|
$0 | References the name of the script. |
$# | Holds the value of the number of positional parameters. |
$* | Lists all of the positional parameters. |
$@ | Means the same as $*, except when enclosed in double quotes. |
"$*" | Expands to a single argument (e.g., "$1 $2 $3"). |
"$@" | Expands to separate arguments (e.g., "$1" "$2" "$3"). |
$1 ${10} | References individual positional parameters. |
(The Script) #!/bin/bash # Scriptname: greetings2 echo "This script is called $0." 1 echo "$0 $1 and $2" echo "The number of positional parameters is $#" ----------------------------------------------------------- (The Command Line) $ chmod +x greetings2 2 $ greetings2 This script is called greetings2. greetings and The number of positional paramters is 3 $ greetings2 Tommy This script is called greetings2. greetings Tommy and The number of positional parameters is 1 4 $ greetings2 Tommy Kimberly This script is called greetings2. greetings Tommy and Kimberly The number of positional parameters is 2
EXPLANATION
|
The set command with arguments resets the positional parameters.[3] Once reset, the old parameter list is lost. To unset all of the positional parameters, use set . $0 is always the name of the script.
(The Script) #!/bin/bash # Scriptname: args # Script to test command line arguments 1 echo The name of this script is $0. 2 echo The arguments are $*. 3 echo The first argument is $1. 4 echo The second argument is $2. 5 echo The number of arguments is $#. 6 oldargs=$* 7 set Jake Nicky Scott # Reset the positional parameters 8 echo All the positional parameters are $*. 9 echo The number of postional parameters is $#. 10 echo "Good bye for now, $1." 11 set $(date) # Reset the positional parameters 12 echo The date is $2 $3, $6. 13 echo "The value of \$oldargs is $oldargs." 14 set $oldargs 15 echo $1 $2 $3 (The Output) $ args a b c d 1 The name of this script is args. 2 The arguments are a b c d. 3 The first argument is a. 4 The second argument is b. 5 The number of arguments is 4. 8 All the positional parameters are Jake Nicky Scott. 9 The number of positional parameters is 3. 10 Good-bye for now, Jake. 12 The date is Mar 25, 2001. 13 The value of $oldargs is a b c d. 15 Wed Mar 25
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: checker # Script to demonstrate the use of special variable # modifiers and arguments 1 name=${1:?"requires an argument" } echo Hello $name (The Command Line) 2 $ checker checker: 1: requires an argument 3 $ checker Sue Hello Sue
EXPLANATION
|
How $* and $@ Differ. The $* and $@ differ only when enclosed in double quotes. When $* is enclosed within double quotes, the parameter list becomes a single string. When $@ is enclosed within double quotes, each of the parameters is quoted; that is, each word is treated as a separate string.
1 $ set 'apple pie' pears peaches 2 $ for i in $* > do > echo $i > done apple pie pears peaches 3 $ set 'apple pie' pears peaches 4 $ for i in "$*" > do > echo $i > done apple pie pears peaches 5 $ set 'apple pie' pears peaches 6 $ for i in $@ > do > echo $i > done apple pie pears peaches 7 $ set 'apple pie' pears peaches 8 $ for i in "$@" # At last!! > do > echo $i > done apple pie pears peaches
EXPLANATION
|
Conditional commands allow you to perform some task(s) based on whether a condition succeeds or fails. The if command is the simplest form of decision-making; the if/else commands allow a two-way decision; and the if/elif/else commands allow a multiway decision.
Bash allows you to test two types of conditions: the success or failure of commands or whether an expression is true or false. In either case, the exit status is always used. An exit status of zero indicates success or true, and an exit status that is nonzero indicates failure or false. The ? status variable contains a numeric value representing the exit status. To refresh your memory on how exit status works, look at Example 12.15.
(At the Command Line) 1 $ name=Tom 2 $ grep "$name" /etc/passwd Tom:8ZKX2F:5102:40:Tom Savage:/home/tom:/bin/sh 3 $ echo $? 0 # Success! 4 $ name=Fred 5 $ grep "$name" /etc/passwd $ echo $? 1 # Failure
EXPLANATION
|
To evaluate an expression, the built-in test command is commonly used. This command is also linked to the bracket symbol. Either the test command itself can be used, or the expression can be enclosed in set of single brackets. Shell metacharacter expansion is not performed on expressions evaluated with the simple test command or when square brackets are used. Since word splitting is performed on variables, strings containing whitespace must be quoted. (See Example 12.16.)
On versions of bash 2.x, double brackets [[ ]] (the built-in compound test command) can be used to evaluate expressions. Word splitting is not performed on variables and pattern matching is done, allowing the expansion of metacharacters. A literal string containing whitespace must be quoted and if a string (with or without whitespace) is to be evaluated as an exact string, rather than part of a pattern, it too must be enclosed in quotes. The logical operators && (and) and || (or) replace the a and o operators used with the simple test command. (See Example 12.17.)
Although the test command can evaluate arithmetic expressions, you may prefer to use the let command with its rich set of C-like operators (bash 2.x). The let command can be represented alternatively by enclosing its expression in a set of double parentheses. (See Example 12.18.)
Whether you are using the test command, compound command, or let command, the result of an expression is tested, with zero status indicating success and nonzero status indicating failure. (See Table 11.10.)
The following examples illustrate how the exit status is tested with the built-in test command and the alternate form of test, a set of single brackets [ ]; the compound command, a set of double brackets [[ ]]; and the let command, a set of double parentheses (( )).
(The test Command) (At the Command Line) 1 $ name=Tom 2 $ grep "$name" /etc/passwd 3 $ echo $? 4 $ test $name != Tom 5 $ echo $? 1 # Failure 6 $ [ $name = Tom ] # Brackets replace the test command 7 $ echo $? 0 8 $ [ $name = [Tt]?? ] $ echo $? 1 9 $ x=5 $ y=20 10 $ [ $x -gt $y ] $ echo $? 1 11 $ [ $x -le $y ] $ echo $? 0
EXPLANATION
|
(The compound test command)(bash 2.x) $ name=Tom; friend=Joseph 1 $ [[ $name == [Tt]om ]] # Wildcards allowed $ echo $? 0 2 $ [[ $name == [Tt]om && $friend == "Jose" ]] $ echo $? 1 3 $ shopt -s extglob # Turns on extended pattern matching 4 $ name=Tommy 5 $ [[ $name == [Tt]o+(m)y ]] $ echo $? 0
EXPLANATION
|
(The let Command) (bash 2.x) (At the Command Line) 1 $ x=2 $ y=3 2 (( x > 2 )) echo $? 1 3 (( x < 2 )) echo $? 0 4 (( x == 2 && y == 3 )) echo $? 0 5 (( x > 2 || y < 3 )) echo $? 1
EXPLANATION
|
Test Operator | Tests True If |
---|---|
String Test | |
[ string1 = string2 ] | String1 is equal to String2 (space surrounding = required). |
[ string1==string2 ] | (Can be used instead of the single = sign on bash versions 2.x.) |
[ string1 != string2 ] | String1 is not equal to String2 (space surrounding != required). |
[ string ] | String is not null. |
[ z string ] | Length of string is zero. |
[ n string ] | Length of string is nonzero. |
[ l string ] | Length of string (number of characters). |
Examples: | test n $word or [ n $word ] test tom = sue or [ tom = sue ] |
Logical Test | |
[ string1 a string1 ] | Both string1 and string2 are true. |
[ string1 o string2 ] | Either string1 or string2 is true. |
[ ! string1 ] | Not a string1 match. |
Logical Test (Compound Test)[a] | |
[[ pattern1 && pattern2 ]] | Both pattern1 and pattern2 are true. |
[[ pattern1 || pattern2 ]] | Either pattern1 or pattern2 is true. |
[[ ! pattern ]] | Not a pattern match. |
Integer Test | |
[ int1 eq int2 ] | Int1 is equal to int2. |
[ int1 ne int2 ] | Int1 is not equal to int2. |
[ int1 gt int2 ] | Int1 is greater than int2. |
[ int1 ge int2 ] | Int1 is greater than or equal to int2. |
[ int1 lt int2 ] | Int1 is less than int2. |
[ int1 le int2 ] | Int1 is less than or equal to int2. |
Binary Operators for File Testing | |
[ file1 nt file2 ] | True if file1 is newer than file2 (according to modification date). |
[ file1 ot file2 ] | True if file1 is older than file2. |
[ file1 ef file2 ] | True if file1and file2 have the same device or inode numbers. |
[a] With the compound test, pattern can contain pattern matching metacharacters; for exact string testing, pattern2 must be enclosed in quotes.
Operator | Meaning |
---|---|
+ | Unary minus and plus. |
! ~ | Logical and bitwise not (negation). |
* / % | Multiply, divide, remainder. |
+ | Add, subtract. |
let Operators Not Implemented Prior to bash 2.x | |
<< >> | Bitwise left shift, right shift. |
<= >= < > | Comparison operators. |
== != | Equal to and not equal to. |
& | Bitwise and. |
^ | Bitwise exclusive or. |
| | Bitwise or. |
&& | Logical and. |
|| | Logical or. |
= *= /= %= += = <<= >>= &= ^= |= | Assignment and shortcut assignment. |
The simplest form of conditional is the if command. The command (a bash built-in or executable) following the if construct is executed and its exit status is returned. The exit status is usually determined by the programmer who wrote the utility. If the exit status is zero, the command succeeded and the statement(s) after the then keyword are executed. In the C shell, the expression following the if command is a Boolean-type expression as in C. But in the Bash, Bourne, and Korn shells, the statement following the if is a command or group of commands. If the exit status of the command being evaluated is zero, the block of statements after the then is executed until fi is reached. The fi terminates the if block. If the exit status is nonzero, meaning that the command failed in some way, the statement(s) after the then keyword are ignored and control goes to the line directly after the fi statement.
It is important that you know the exit status of the commands being tested. For example, the exit status of grep is reliable in letting you know whether grep found the pattern it was searching for in a file. If grep is successful in its search, it returns a zero exit status; if not, it returns one. The sed and gawk programs also search for patterns, but they will report a successful exit status regardless of whether they find the pattern. The criterion for success with sed and gawk is correct syntax, not functionality.
FORMATif command then command command fi ------------------------------------ (Using test for numbers and strings -- old format) if test expression then command fi or if [ string/numeric expression ] then command fi ------------------------------------- (Using test for strings -- new formst) if [[ string expression ]] then command fi (Using let for numbers -- new format) if (( numeric expression )) -------------------------------------- |
1 if grep "$name" /etc/passwd > /dev/null 2>&1 2 then echo Found $name! 3 fi
EXPLANATION
|
1 echo "Are you o.k. (y/n) ?" read answer 2 if [ "$answer" = Y -o "$answer" = y ] then echo "Glad to hear it." 3 fi 4 if [ $answer = Y -o "$answer" = y ] [: too many arguments ------------------------------------------- 5 if [[ $answer == [Yy]* || $answer == Maybe ]][a] then echo "Glad to hear it." fi 6 shopt -s extglob 7 answer="not really" 8 if [[ $answer = [Nn]o?( way|t really) ]] then echo "So sorry. " fi
[a] Lines 5 through 8 are only implemented on versions of bash 2.x.
EXPLANATION
|
The exit Command and the ? Variable. The exit command is used to terminate the script and return to the command line. You may want the script to exit if some condition occurs. The argument given to the exit command is a number ranging from 0 to 255. If the program exits with zero as an argument, the program exited with success. A nonzero argument indicates some kind of failure. The argument given to the exit command is stored in the shell's ? variable.
(The Script) $ cat bigfiles # Name: bigfiles # Purpose: Use the find command to find any files in the root # partition that have not been modified within the past n (any # number within 30 days) days and are larger than 20 blocks # (512-byte blocks) 1 if (( $# != 2 ))[a] # [ $# -ne 2 ] then echo "Usage: $0 mdays size " 1>&2 exit 1 2 fi 3 if (( $1 < 0 || $1 > 30 ))[b] # [ $1 -lt 0 -o $1 -gt 30 ] then echo "mdays is out of range" exit 2 4 fi 5 if (( $2 <= 20 )) # [ $2 -le 20 ] then echo "size is out of range" exit 3 6 fi 7 find / -xdev -mtime $1 -size +$2 (The Command Line) $ bigfiles Usage: bigfiles mdays size $ echo $? 1 $ bigfiles 400 80 mdays is out of range $ echo $? 2 $ bigfiles 25 2 size is out of range $ echo $? 3 $ bigfiles 2 25 (Output of find prints here)
[a] Not implemented on versions prior to bash 2.x. On older versions could also be written if let $(( $# != 2 )).
[b] Same as above. On older versions could also be written if let $(( $1 < 0 || $1 > 30 ).
EXPLANATION
|
Checking for Null Values. When checking for null values in a variable, use double quotes to hold the null value or the test command will fail.
(The Script) 1 if [ "$name" = "" ] # Alternative to [ ! "$name" ] # or [ -z "$name" ] then echo The name variable is null fi (From System showmount program, which displays all remotely mounted systems) remotes=$(/usr/sbin/showmount) 2 if [ "X${remotes}" != "X" ] then /usr/sbin/wall ${remotes} ... 3 fi
EXPLANATION
|
Nested if Commands. When if statements are nested, the fi statement always goes with the nearest if statement. Indenting the nested ifs makes it easier to see which if statement goes with which fi statement.
The if/else commands allow a two-way decision-making process. If the command after the if fails, the commands after the else are executed.
FORMATif command then command(s) else command(s) fi |
(The Script) #!/bin/bash # Scriptname: grepit 1 if grep "$name" /etc/passwd >& /dev/null; then echo Found $name! 3 else 4 echo "Can't find $name." exit 1 5 fi
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: idcheck # purpose:check user id to see if user is root. # Only root has a uid of 0. # Format for id output:uid=9496(ellie) gid=40 groups=40 # root's uid=0 1 id='id | gawk F'[=(]' '{print $2}'' # get user id echo your user id is: $id 2 if (( id == 0 ))[a] # [ $id eq 0 ] (See cd file: idcheck2) then 3 echo "you are superuser." 4 else echo "you are not superuser." 5 fi (The Command Line) 6 $ idcheck your user id is: 9496 you are not superuser. 7 $ su Password: 8 # idcheck your user id is: 0 you are superuser
[a] Not implemented on versions of bash prior to 2.x.
EXPLANATION
|
The if/elif/else commands allow a multiway decision-making process. If the command following the if fails, the command following the elif is tested. If that command succeeds, the commands under its then statement are executed. If the command after the elif fails, the next elif command is checked. If none of the commands succeeds, the else commands are executed. The else block is called the default.
FORMATif command then command(s) elif command then commands(s) elif command then command(s) else command(s) fi |
(The Script) #!/bin/bash # Scriptname: tellme # Using the old-style test command 1 echo -n "How old are you? " read age 2 if [ $age -lt 0 -o $age -gt 120 ] then echo "Welcome to our planet! " exit 1 fi 3 if [ $age -ge 0 -a $age -le 12 ] then echo "A child is a garden of verses" elif [ $age -ge 12 -a $age -le 19 ] then echo "Rebel without a cause" elif [ $age -ge 20 -a $age -le 29 ] then echo "You got the world by the tail!!" elif [ $age -ge 30 -a $age -le 39 ] then echo "Thirty something..." 4 else echo "Sorry I asked" 5 fi (The Output) $ tellme How old are you? 200 Welcome to our planet! $ tellme How old are you? 13 Rebel without a cause $ tellme How old are you? 55 Sorry I asked ---------------------------------------------------------- #!/bin/bash # Using the new (( )) compound let command # Scriptname: tellme2 1 echo -n "How old are you? " read age 2 if (( age < 0 || age > 120 )) then echo "Welcome to our planet! " exit 1 fi 3 if ((age >= 0 && age <= 12)) then echo "A child is a garden of verses" elif ((age >= 13 && age <= 19 )) then echo "Rebel without a cause" elif (( age >= 19 && age <= 29 )) then echo "You got the world by the tail!!" elif (( age >= 30 && age <= 39 )) then echo "Thirty something..." 4 else echo "Sorry I asked" 5 fi
EXPLANATION
|
Often when you are writing scripts, your script will require that there are certain files available and that those files have specific permissions, are of a certain type, or have other attributes. You will find file testing a necessary part of writing dependable scripts.
Test Operator | Tests True If |
---|---|
b filename | Block special file. |
c filename | Character special file. |
d filename | Directory existence. |
e filename | File existence. |
f filename | Regular file existence and not a directory. |
G filename | True if file exists and is owned by the effective group ID. |
g filename | Set-group-ID is set. |
k filename | Sticky bit is set. |
L filename | File is a symbolic link. |
p filename | File is a named pipe. |
O filename | File exists and is owned by the effective user ID. |
r filename | File is readable. |
S filename | File is a socket. |
s filename | File is nonzero size. |
t fd | True if fd (file descriptor) is opened on a terminal. |
u filename | Set-user-ID bit is set. |
w filename | File is writeable. |
x filename | File is executable. |
(The Script) #!/bin/bash # Using the old style test command # filename: perm_check file=./testing 1 if [ -d $file ] then echo "$file is a directory" 2 elif [ -f $file ] then 3 if [ -r $file -a -w $file -a -x $file ] then # nested if command echo "You have read,write,and execute \ permission on $file." 4 fi 5 else echo "$file is neither a file nor a directory. " 6 fi ---------------------------------------------------------- #!/bin/bash # Using the new compound operator for test (( ))[a] # filename: perm_check2 file=./testing 1 if [[ -d $file ]] then echo "$file is a directory" 2 elif [[ -f $file ]] then 3 if [[ -r $file && -w $file && -x $file ]] then # nested if command echo "You have read,write,and execute \ permission on $file." 4 fi 5 else echo "$file is neither a file nor a directory. " 6 fi
[a] New-style test with compound operators not implemented before bash 2.x.
EXPLANATION
|
The null command, represented by a colon, is a built-in, do-nothing command that returns an exit status of zero. It is used as a placeholder after an if command when you have nothing to say, but need a command or the program will produce an error message because it requires something after the then statement. Often the null command is used as an argument to the loop command to make the loop a forever loop.
(The Script) #!/bin/bash # filename: name_grep 1 name=Tom 2 if grep "$name" databasefile >& /dev/null then 3 : 4 else echo "$1 not found in databasefile" exit 1 fi
EXPLANATION
|
(The Command Line) 1 $ DATAFILE= 2 $ : ${DATAFILE:=$HOME/db/datafile} $ echo $DATAFILE /home/jody/ellie/db/datafile 3 $ : ${DATAFILE:=$HOME/junk} $ echo $DATAFILE /home/jody/ellie/db/datafile
EXPLANATION
|
(The Script) #!/bin/bash 1 # Scriptname: wholenum # Purpose:The expr command tests that the user enters an # integer # echo "Enter an integer." read number 2 if expr "$number" + 0 >& /dev/null then 3 : else 4 echo "You did not enter an integer value." exit 1 5 fi
EXPLANATION
|
The case command is a multiway branching command used as an alternative to if/elif commands. The value of the case variable is matched against value1, value2, and so forth, until a match is found. When a value matches the case variable, the commands following the value are executed until the double semicolons are reached. Then execution starts after the word esac (case spelled backward).
If the case variable is not matched, the program executes the commands after the *), the default value, until ;; or esac is reached. The *) value functions the same as the else statement in if/else conditionals. The case values allow the use of shell wildcards and the vertical bar (pipe symbol) for oring two values.
FORMATcase variable in value1) command(s) ;; value2) command(s) ;; *) command(s) ;; esac |
(The Script) #!/bin/bash # Scriptname: xcolors 1 echo -n "Choose a foreground color for your xterm window: " read color 2 case "$color" in 3 [Bb]l??) 4 xterm -fg blue -fn terminal & 5 ;; 6 [Gg]ree*) xterm -fg darkgreen -fn terminal & ;; 7 red | orange) # The vertical bar means "or" xterm -fg "$color" -fn terminal & ;; 8 *) xterm -fn terminal ;; 9 esac 10 echo "Out of case command"
EXPLANATION
|
Creating Menus with the here document and case Command. The here document and case command are often used together. The here document is used to create a menu of choices that will be displayed to the screen. The user will be asked to select one of the menu items, and the case command will test against the set of choices to execute the appropriate command.
(From the .bash_profile File) echo "Select a terminal type: " 1 cat <<- ENDIT 1) unix 2) xterm 3) sun 2 ENDIT 3 read choice 4 case "$choice" in 5 1) TERM=unix export TERM ;; 2) TERM=xterm export TERM ;; 6 3) TERM=sun export TERM ;; 7 esac 8 echo "TERM is $TERM." (The Output) $ . .bash_profile Select a terminal type: 1) unix 2) xterm 3) sun 2 <-- User input TERM is xterm.
EXPLANATION
|
Looping commands are used to execute a command or group of commands a set number of times or until a certain condition is met. The bash shell has three types of loops: the for loop, the while loop, and the until loop.
The for looping command is used to execute commands a finite number of times on a list of items. For example, you might use this loop to execute the same commands on a list of files or usernames. The for command is followed by a user-defined variable, the keyword in, and a list of words. The first time in the loop, the first word from the wordlist is assigned to the variable, and then shifted off the list. Once the word is assigned to the variable, the body of the loop is entered, and commands between the do and done keywords are executed. The next time around the loop, the second word is assigned to the variable, and so on. The body of the loop starts at the do keyword and ends at the done keyword. When all of the words in the list have been shifted off, the loop ends and program control continues after the done keyword.
FORMATfor variable in word_list do command(s) done |
(The Script) #!/bin/bash # Scriptname: forloop 1 for pal in Tom Dick Harry Joe 2 do 3 echo "Hi $pal" 4 done 5 echo "Out of loop" (The Output) $ forloop Hi Tom Hi Dick Hi Harry Hi Joe Out of loop
EXPLANATION
|
(The Command Line) 1 $ cat mylist tom patty ann jake (The Script) #!/bin/bash # Scriptname: mailer 2 for person in $(cat mylist) do 3 mail $person < letter echo $person was sent a letter. 4 done 5 echo "The letter has been sent."
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: backup # Purpose: Create backup files and store # them in a backup directory. # 1 dir=/home/jody/ellie/backupscripts 2 for file in memo[1-5] do 3 if [ -f $file ] then cp $file $dir/$file.bak echo "$file is backed up in $dir" fi 4 done (The Output) memo1 is backed up in /home/jody/ellie/backupscripts memo2 is backed up in /home/jody/ellie/backupscripts memo3 is backed up in /home/jody/ellie/backupscripts memo4 is backed up in /home/jody/ellie/backupscripts memo5 is backed up in /home/jody/ellie/backupscripts
EXPLANATION
|
The $* and $@ Variables in Wordlists. When expanded, the $* and $@ are the same unless enclosed in double quotes. $* evaluates to one string, whereas $@ evaluates to a list of separate words.
(The Script) #!/bin/bash # Scriptname: greet 1 for name in $* # same as for name in $@ 2 do echo Hi $name 3 done (The Command Line) $ greet Dee Bert Lizzy Tommy Hi Dee Hi Bert Hi Lizzy Hi Tommy
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: permx 1 for file # Empty wordlist do 2 if [ -f $file -a ! -x $file ] or if [[ -f $file && ! -x $file ]][a] then 3 chmod +x $file echo $file now has execute permission fi done (The Command Line) 4 $ permx * addon now has execute permission checkon now has execute permission doit now has execute permission
[a] Bash 2.x only.
EXPLANATION
|
The while command evaluates the command following it and, if its exit status is zero, the commands in the body of the loop (commands between do and done) are executed. When the done keyword is reached, control is returned to the top of the loop and the while command checks the exit status of the command again. Until the exit status of the command being evaluated by the while becomes nonzero, the loop continues. When the exit status reaches nonzero, program execution starts after the done keyword.
FORMATwhile command do command(s) done |
(The Script) #!/bin/bash # Scriptname: num 1 num=0 # Initialize num 2 while (( $num < 10 ))[a] # or while [ num -lt 10 ] do echo -n "$num " 3 let num+=1 # Increment num done 4 echo -e "\nAfter loop exits, continue running here" (The Output) 0 1 2 3 4 5 6 7 8 9 4 After loop exits, continue running here
[a] Versions of bash 2.x use this form.
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: quiz 1 echo "Who was the 2nd U.S. president to be impeached?" read answer 2 while [[ "$answer" != "Bill Clinton" ]] 3 do echo "Wrong try again!" 4 read answer 5 done 6 echo You got it! (The Output) $ quiz Who was the 2nd U.S. president to be impeached? Ronald Reagan Wrong try again! Who was the 2nd U.S. president to be impeached? I give up Wrong try again! Who was the 2nd U.S. president to be impeached? Bill Clinton You got it!
EXPLANATION
|
(The Script) $ cat sayit #!/bin/bash # Scriptname: sayit echo Type q to quit. go=start 1 while [ -n "$go" ] # Make sure to double quote the variable do 2 echo -n I love you. 3 read word 4 if [[ $word == [Qq] ]] then # [ "$word" = q -o "$word" = Q ] Old style echo "I'll always love you!" go= fi done (The Output) $ sayit Type q to quit. I love you. <-- When user presses Enter, the program continues I love you. I love you I love you. I love you.q I'll always love you! $
|
The until command is used like the while command, but executes the loop statements only if the command after until fails, i.e., if the command returns an exit status of nonzero. When the done keyword is reached, control is returned to the top of the loop and the until command checks the exit status of the command again. Until the exit status of the command being evaluated by until becomes zero, the loop continues. When the exit status reaches zero, the loop exits, and program execution starts after the done keyword.
FORMATuntil command do command(s) done |
#!/bin/bash 1 until who | grep linda 2 do sleep 5 3 done talk linda@dragonwings
EXPLANATION
|
(The Script) $ cat hour #!/bin/bash # Scriptname: hour 1 let hour=0 2 until (( hour > 24 ))[a] # or [ $hour -gt 24 ] do 3 case "$hour" in [0-9]|1[0-1]) echo "Good morning!" ;; 12) echo "Lunch time." ;; 1[3-7]) echo "Siesta time." ;; *) echo "Good night." ;; esac 4 let hour+=1 # Don't forget to increment the hour 5 done (The Output) $ hour Good morning! Good morning ... Lunch time. Siesta time. ... Good night. ...
[a] Versions of bash 2.x use this form.
EXPLANATION
|
The here document is an easy method for creating menus, but bash introduces another looping mechanism, called the select loop, used primarily for creating menus. A menu of numerically listed items is displayed to standard error. The PS3 prompt is used to prompt the user for input; by default, PS3 is #?. After the PS3 prompt is displayed, the shell waits for user input. The input should be one of the numbers in the menu list. The input is stored in the special shell REPLY variable. The number in the REPLY variable is associated with the string to the right of the parentheses in the list of selections.
The case command is used with the select command to allow the user to make a selection from the menu and, based on that selection, execute commands. The LINES and COLUMNS variables can be used to determine the layout of the menu items displayed on the terminal. (These variables are built in to versions of bash 2.x, but are not built in to earlier versions; if they have not been defined, you can define and export them in the .bash_profile.) The output is displayed to standard error, each item preceded by a number and closing parenthesis, and the PS3 prompt is displayed at the bottom of the menu. Because the select command is a looping command, it is important to remember to use either the break command to get out of the loop, or the exit command to exit the script.
FORMATselect var in wordlist do command(s) done |
(The Script) #!/bin/bash # Scriptname: runit 1 PS3="Select a program to execute: " 2 select program in 'ls -F' pwd date 3 do 4 $program 5 done (The Command Line) Select a program to execute: 2 1) ls -F 2) pwd 3) date /home/ellie Select a program to execute: 1 1) ls -F 2) pwd 3) date 12abcrty abc12 doit* progs/ xyz Select a program to execute: 3 1) ls -F 2) pwd 3) date Sun Mar 12 13:28:25 PST 2001
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname name: goodboys 1 PS3="Please choose one of the three boys : " 2 select choice in tom dan guy 3 do 4 case $choice in tom) echo Tom is a cool dude! 5 break;; # break out of the select loop 6 dan | guy ) echo Dan and Guy are both wonderful. break;; *) 7 echo "$REPLY is not one of your choices" 1>&2 echo "Try again." ;; 8 esac 9 done (The Command Line) $ goodboys 1) tom 2) dan 3) guy Please choose one of the three boys : 2 Dan and Guy are both wonderful. $ goodboys 1) tom 2) dan 3) guy Please choose one of the three boys : 4 4 is not one of your choices Try again. Please choose one of the three boys : 1 Tom is a cool dude! $
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: ttype # Purpose: set the terminal type # Author: Andy Admin 1 COLUMNS=60 2 LINES=1 3 PS3="Please enter the terminal type: " 4 select choice in wyse50 vt200 xterm sun do 5 case $REPLY in 1) 6 export TERM=$choice echo "TERM=$choice" break;; # break out of the select loop 2 | 3 ) export TERM=$choice echo "TERM=$choice" break;; 4) export TERM=$choice echo "TERM=$choice" break;; *) 7 echo -e "$REPLY is not a valid choice. Try again\n" 1>&2 8 REPLY= # Causes the menu to be redisplayed ;; esac 9 done (The Command Line) $ ttype 1) wyse50 2) vt200 3) xterm 4) sun Please enter the terminal type : 4 TERM=sun $ ttype 1) wyse50 2) vt200 3) xterm 4) sun Please enter the terminal type : 3 TERM=xterm $ ttype 1) wyse50 2) vt200 3) xterm 4) sun Please enter the terminal type : 7 7 is not a valid choice. Try again. 1) wyse50 2) vt200 3) xterm 4) sun Please enter the terminal type: 2 TERM=vt200
EXPLANATION
|
If some condition occurs, you may want to break out of a loop, return to the top of the loop, or provide a way to stop an infinite loop. The bash shell provides loop control commands to handle these kinds of situations.
The shift Command. The shift command shifts the parameter list to the left a specified number of times. The shift command without an argument shifts the parameter list once to the left. Once the list is shifted, the parameter is removed permanently. Often, the shift command is used in a while loop when iterating through a list of positional parameters.
FORMATshift [n] |
(Without a Loop) (The Script) #!/bin/bash # Scriptname: shifter 1 set joe mary tom sam 2 shift 3 echo $* 4 set $(date) 5 echo $* 6 shift 5 7 echo $* 8 shift 2 (The Output) 3 mary tom sam 5 Thu Mar 16 10:00:12 PST 2001 7 2001 8 shift: shift count must be <= $#
EXPLANATION
|
(With a Loop) (The Script) #!/bin/bash # Name: doit # Purpose: shift through command line arguments # Usage: doit [args] 1 while (( $# > 0 ))[a] # or [ $# -gt 0 ] do 2 echo $* 3 shift 4 done (The Command Line) $ doit a b c d e a b c d e b c d e c d e d e e
[a] Not implemented of versions of bash prior to 2.x.
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: dater # Purpose: set positional parameters with the set command # and shift through the parameters. 1 set $(date) 2 while (( $# > 0 )) # or [ $# -gt 0 ] Old style do 3 echo $1 4 shift done (The Output) $ dater Wed Mar 15 19:25:00 PST 2001
EXPLANATION
|
The break Command. The built-in break command is used to force immediate exit from a loop, but not from a program. (To leave a program, the exit command is used.) After the break command is executed, control starts after the done keyword. The break command causes an exit from the innermost loop, so if you have nested loops, the break command takes a number as an argument, allowing you to break out of a specific outer loop. If you are nested in three loops, the outermost loop is loop number 3, the next nested loop is loop number 2, and the innermost nested loop is loop number 1. The break is useful for exiting from an infinite loop.
FORMATbreak [n] |
EXPLANATION
|
The continue Command. The continue command returns control to the top of the loop if some condition becomes true. All commands below the continue will be ignored. If nested within a number of loops, the continue command returns control to the innermost loop. If a number is given as its argument, control can then be started at the top of any loop. If you are nested in three loops, the outermost loop is loop number 3, the next nested loop is loop number 2, and the innermost nested loop is loop number 1.[4]
FORMATcontinue [n] |
EXPLANATION
|
Nested Loops and Loop Control. When you are using nested loops, the break and continue commands can be given a numeric, integer argument so that control can go from the inner loop to an outer loop.
EXPLANATION
|
Input can be piped or redirected to a loop from a file. Output can also be piped or redirected to a file from a loop. The shell starts a subshell to handle I/O redirection and pipes. Any variables defined within the loop will not be known to the rest of the script when the loop terminates.
Redirecting the Output of the Loop to a File. Output from a bash loop can be sent to a file rather than to the screen. See Example 12.51.
(The Command Line) 1 $ cat memo abc def ghi (The Script) #!/bin/bash # Program name: numberit # Put line numbers on all lines of memo 2 if let $(( $# < 1 )) then 3 echo "Usage: $0 filename " >&2 exit 1 fi 4 count=1 # Initialize count 5 cat $1 | while read line # Input is coming from file provided at command line do 6 let $((count == 1)) && echo "Processing file $1..." > /dev/tty 7 echo -e "$count\t$line" 8 let count+=1 9 done > tmp$$ # Output is going to a temporary file 10 mv tmp$$ $1 (The Command Line) 11 $ numberit memo Processing file memo... 12 $ cat memo 1 abc 2 def 3 ghi
EXPLANATION
|
[a] $$ expands to the PID number of the current shell. By appending this number to the filename, the filename is made unique.
Piping the Output of a Loop to a UNIX Command. Output can be either piped to another command(s) or redirected to a file.
(The Script) #!/bin/bash 1 for i in 7 9 2 3 4 5 2 do echo $i 3 done | sort n (The Output) 2 3 4 5 7 9
EXPLANATION
|
Running Loops in the Background. Loops can be executed to run in the background. The program can continue without waiting for the loop to finish processing.
(The Script) #!/bin/bash 1 for person in bob jim joe sam do 2 mail $person < memo 3 done &
EXPLANATION
|
The shell's internal field separator (IFS) evaluates to spaces, tabs, and the newline character. It is used as a word (token) separator for commands that parse lists of words, such as read, set, and for. It can be reset by the user if a different separator will be used in a list. Before changing its value, it is a good idea to save the original value of the IFS in another variable. Then it is easy to return to its default value, if needed.
(The Script ) $ cat runit2 #/bin/bash # Script is called runit. # IFS is the internal field separator and defaults to # spaces, tabs, and newlines. # In this script it is changed to a colon. 1 names=Tom:Dick:Harry:John 2 oldifs="$IFS" # Save the original value of IFS 3 IFS=":" 4 for persons in $names do 5 echo Hi $persons done 6 IFS="$oldifs" # Reset the IFS to old value 7 set Jill Jane Jolene # Set positional parameters 8 for girl in $* do 9 echo Howdy $girl done (The Output) $ runit2 5 Hi Tom Hi Dick Hi Harry Hi John 9 Howdy Jill Howdy Jane Howdy Jolene
EXPLANATION
|
Functions were introduced to the Bourne shell in AT&T's UNIX SystemVR2 and have been enhanced in the Bourne Again shell. A function is a name for a command or group of commands. Functions are used to modularize your program and make it more efficient. They are executed in context of the current shell. In other words, a child process is not spawned as it is when running an executable program such as ls. You may even store functions in another file and load them into your script when you are ready to use them.
Here is a review of some of the important rules about using functions.
The shell determines whether you are using an alias, a function, a built-in command, or an executable program (or script) found on the disk. It looks for aliases first, then functions, built-in commands, and last, executables.
A function must be defined before it is used.
The function runs in the current environment; it shares variables with the script that invoked it, and lets you pass arguments by assigning them as positional parameters. Local variables can be created within a function by using the local function.
If you use the exit command in a function, you exit the entire script. If you exit the function, you return to where the script left off when the function was invoked.
The return statement in a function returns the exit status of the last command executed within the function or the value of the argument given.
Functions can be exported to subshells with the export f built-in command.
To list functions and definitions, use the declare f command. To list just function names, use declare F.[5]
Traps, like variables, are global within functions. They are shared by both the script and the functions invoked in the script. If a trap is defined in a function, it is also shared by the script. This could have unwanted side effects.
If functions are stored in another file, they can be loaded into the current script with the source or dot command.
Functions can be recursive; i.e., they can call themselves. There is no limit imposed for the number of recursive calls.
FORMATfunction function_name { commands ; commands; } |
function dir { echo "Directories: ";ls -l|awk '/^d/ {print $NF}'; }
EXPLANATIONThe keyword function is followed by the name of the function dir. (Sometimes empty parentheses follow the function name, but they are not necessary.) The commands within the curly braces will be executed when dir is typed. The purpose of the function is to list only the subdirectories below the present working directory. The spaces surrounding the first curly brace are required. |
To Unset a Function. To remove a function from memory, use the unset command.
FORMATunset -f function_name |
Exporting Functions. Functions may be exported so that subshells know about them.
FORMATexport -f function_name |
Because the function is executed within the current shell, the variables will be known to both the function and the shell. Any changes made to your environment in the function will also be made to the shell.
Arguments. Arguments can be passed to functions by using positional parameters. The positional parameters are private to the function; that is, arguments to the function will not affect any positional parameters used outside the function. See Example 12.56.
The Built-In local Function. To create local variables that are private to the function and will disappear after the function exits, use the built-in local function. See Example 12.57.
The Built-In return Function. The return command can be used to exit the function and return control to the program at the place where the function was invoked. (Remember, if you use exit anywhere in your script, including within a function, the script terminates.) The return value of a function is really just the value of the exit status of the last command in the script, unless you give a specific argument to the return command. If a value is assigned to the return command, that value is stored in the ? variable and can hold an integer value between zero and 256. Because the return command is limited to returning only an integer between zero and 256, you can use command substitution to capture the output of a function. Place the entire function in parentheses preceded by a $, e.g., $(function_name), or traditional backquotes to capture and assign the output to a variable just as you would if getting the output of a UNIX command.
(Passing Arguments) (The Script) #!/bin/bash # Scriptname: checker # Purpose: Demonstrate function and arguments 1 function Usage { echo "error: $*" 2>&1; exit 1; } 2 if (( $# != 2 )) then 3 Usage "$0: requires two arguments" fi 4 if [[ ! ( -r $1 && -w $1 ) ]] then 5 Usage "$1: not readable and writeable" fi 6 echo The arguments are: $* < Program continues here > (Output) $ checker error: checker: requires two arguments $ checker file1 file2 error: file1: not readable and writeable $ checker filex file2 The arguments are filex file2
EXPLANATION
|
[a] With the old test form, the expression is written if [ ! \( -r $1 -a -w $1 \) ].
(Using the return Command) (The Script) $ cat do_increment #!/bin/bash # Scriptname: do_increment 1 increment () { 2 local sum; # sum is known only in this function 3 let "sum=$1 + 1" 4 return $sum # Return the value of sum to the script. } 5 echo n "The sum is " 6 increment 5 # Call function increment; pass 5 as a # parameter. 5 becomes $1 for the increment # function. 7 echo $? # The return value is stored in $? 8 echo $sum # The variable "sum" is not known here (The Output) $ do_increment 4,6 The sum is 6 8
EXPLANATION
|
(Using Command Substitution) (The Script) $ cat do_square #!/bin/bash # Scriptname: do_square 1 function square { local sq # sq is local to the function let "sq=$1 * $1" echo "Number to be squared is $1." 2 echo "The result is $sq " } 3 echo "Give me a number to square. " read number 4 value_returned=$(square $number) # Command substitution 5 echo "$value_returned" (The Output) $ do_square 3 Give me a number to square. 10 5 Number to be squared is 10. The result is 100
EXPLANATION
|
Storing Functions. Functions are often defined in the .profile file so that when you log in, they will be defined. Functions can be exported, and they can be stored in a file. Then when you need the function, the source or dot command is used with the name of the file to activate the definitions of the functions within it.
1 $ cat myfunctions 2 function go() { cd $HOME/bin/prog PS1=''pwd' > ' ls } 3 function greetings() { echo "Hi $1! Welcome to my world." ; } 4 $ source myfunctions 5 $ greetings george Hi george! Welcome to my world.
EXPLANATION
|
(The dbfunctions file shown below contains functions to be used by the main program. See cd for complete script.) 1 $ cat dbfunctions 2 function addon () { # Function defined in file dbfunctions 3 while true do echo "Adding information " echo "Type the full name of employee " read name echo "Type address for employee " read address echo "Type start date for employee (4/10/88 ) :" read startdate echo $name:$address:$startdate echo n "Is this correct? " read ans case "$ans" in [Yy]*) echo "Adding info..." echo $name:$address:$startdate>>datafile sort u datafile o datafile echo n "Do you want to go back to the main menu? " read ans if [[ $ans == [Yy] ]] then 4 return # Return to calling program else 5 continue # Go to the top of the loop fi ;; *) echo "Do you want to try again? " read answer case "$answer" in [Yy]*) continue;; *) exit;; esac ;; esac done 6 } # End of function definition ------------------------------------------------------------- (The Command Line) 7 $ more mainprog #!/bin/bash # Scriptname: mainprog # This is the main script that will call the function, addon datafile=$HOME/bourne/datafile 8 source dbfunctions # The file is loaded into memory if [ ! e $datafile ] then echo "$(basename $datafile) does not exist" >&2 exit 1 fi 9 echo "Select one: " cat <<EOF [1] Add info [2] Delete info [3] Update info [4] Exit EOF read choice case $choice in 10 1) addon # Calling the addon function ;; 2) delete # Calling the delete function ;; 3) update ;; 4) echo Bye exit 0 ;; *) echo Bad choice exit 2 ;; esac echo Returned from function call echo The name is $name # Variable set in the function are known in this shell. done
EXPLANATION
|
While your program is running, if you press Control-C or Control-\, your program terminates as soon as the signal arrives. There are times when you would rather not have the program terminate immediately after the signal arrives. You could arrange to ignore the signal and keep running or perform some sort of cleanup operation before actually exiting the script. The trap command allows you to control the way a program behaves when it receives a signal.
A signal is defined as an asynchronous message that consists of a number that can be sent from one process to another, or by the operating system to a process if certain keys are pressed or if something exceptional happens.[6] The trap command tells the shell to terminate the command currently in execution upon the receipt of a signal. If the trap command is followed by commands within quotes, the command string will be executed upon receipt of a specified signal. The shell reads the command string twice, once when the trap is set, and again when the signal arrives. If the command string is surrounded by double quotes, all variable and command substitution will be performed when the trap is set the first time. If single quotes enclose the command string, variable and command substitution do not take place until the signal is detected and the trap is executed.
Use the command kill l or trap l to get a list of all signals. Table 12.6 provides a list of signal numbers and their corresponding names.
FORMATtrap 'command; command' signal-number trap 'command; command' signal-name |
trap 'rm tmp*; exit 1' 0 1 2 15 trap 'rm tmp*; exit 1' EXIT HUP INT TERM
EXPLANATIONWhen any of the signals 1 (hangup), 2 (interrupt), or 15 (software termination) arrives, remove all the tmp files and exit. |
If an interrupt comes in while the script is running, the trap command lets you handle the interrupt signal in several ways. You can let the signal behave normally (default), ignore the signal, or create a handler function to be called when the signal arrives.
Signal names such as HUP and INT are normally prefixed with SIG, for example, SIGHUP, SIGINT, and so forth.[7] The bash shell allows you to use symbolic names for the signals, which are the signal names without the SIG prefix, or you can use the numeric value for the signal. See Table 12.6. A pseudo signal name EXIT, or the number 0, will cause the trap to be executed when the shell exits.
1) SIGHUP | 9) SIGKILL | 18) SIGCONT | 26) SIGVTALRM |
2) SIGINT | 10) SIGUSR1 | 19) SIGSTOP | 27) SIGPROF |
3) SIGQUIT | 11) SIGSEGV | 20) SIGTSTP | 28) SIGWINCH |
4) SIGILL | 12) SIGUSR2 | 21) SIGTTIN | 29) SIGIO |
5) SIGTRAP | 13) SIGPIPE | 22) SIGTTOU | 30) SIGPWR |
6) SIGABRT | 14) SIGALRM | 23) SIGURG | |
7) SIGBUS | 15) SIGTERM | 24) SIGXCPU | |
8) SIGFPE | 17) SIGCHLD | 25) SIGXFSZ |
Resetting Signals. To reset a signal to its default behavior, the trap command is followed by the signal name or number. Traps set in functions are recognized by the shell that invoked the function, once the function has been called. Any traps set outside the function are also recognized with the function.
trap 2 or trap INT
EXPLANATIONResets the default action for signal 2, SIGINT. The default action is to kill the process when the interrupt key (Control-C) is pressed. |
Ignoring Signals . If the trap command is followed by a pair of empty quotes, the signals listed will be ignored by the process.
trap " " 1 2 or trap "" HUP INT
EXPLANATIONSignals 1 (SIGHUP) and 2 (SIGINT) will be ignored by the shell process. |
Listing Traps. To list all traps and the commands assigned to them, type trap.
(At the command line) 1 $ trap 'echo "Caught ya!; exit"' 2 2 $ trap trap -- 'echo "Caught ya!; exit 1"' SIGINT 3 $ trap -
EXPLANATION
|
(The Script) #!/bin/bash # Scriptname: trapping # Script to illustrate the trap command and signals # Can use the signal numbers or bash abbreviations seen # below. Cannot use SIGINT, SIGQUIT, etc. 1 trap 'echo "Control C will not terminate $0."' INT 2 trap 'echo "Control \ will not terminate $0."' QUIT 3 trap 'echo "Control Z will not terminate $0."' TSTP 4 echo "Enter any string after the prompt. When you are ready to exit, type \"stop\"." 5 while true do 6 echo n "Go ahead...> " 7 read 8 if [[ $REPLY == [Ss]top ]] then 9 break fi 10 done (The Output) $ trapping 4 Enter any string after the prompt. When you are ready to exit, type "stop". 6 Go ahead...> this is it^C 1 Control C will not terminate trapping. 6 Go ahead...> this is it again^Z 3 Control Z will not terminate trapping. 6 Go ahead...> this is never it|^\ 2 Control \ will not terminate trapping. 6 Go ahead...> stop $
EXPLANATION
|
Resetting Signals. To reset a signal to its default behavior, the trap command is followed by the signal name or number.
trap 2
EXPLANATIONResets the default action for signal 2, SIGINT, which is used to kill a process, i.e., Control-C. |
trap 'trap 2' 2
EXPLANATIONSets the default action for signal 2 (SIGINT) to execute the command string within quotes when the signal arrives. The user must press Control-C twice to terminate the program. The first trap catches the signal, the second trap resets the trap back to its default action, which is to kill the process. |
Traps in Functions. If you use a trap to handle a signal in a function, it will affect the entire script, once the function is called. The trap is global to the script. In the following example, the trap is set to ignore the interrupt key, ^C. This script had to be killed with the kill command to stop the looping. It demonstrates potential undesirable side effects when using traps in functions.
(The Script) #!/bin/bash 1 function trapper () { echo "In trapper" 2 trap 'echo "Caught in a trap!"' INT # Once set, this trap affects the entire script. Anytime # ^C is entered, the script will ignore it. } 3 while : do echo "In the main script" 4 trapper 5 echo "Still in main" sleep 5 done (The Output) $ trapper In the main script In trapper Still in main ^CCaught in a trap! In the main script In trapper Still in main ^CCaught in a trap! In the main script
EXPLANATION
|
By using the n option to the bash command, you can check the syntax of your scripts without really executing any of the commands. If there is a syntax error in the script, the shell will report the error. If there are no errors, nothing is displayed.
The most commonly used method for debugging scripts is the set command with the x option, or bash invoked with the x option and the scriptname. See Table 12.7 for a list of debugging options. These options allow an execution trace of your script. Each command from your script is displayed after substitution has been performed, and then the command is executed. When a line from your script is displayed, it is preceded with a plus (+) sign.
With the verbose option turned on, or by invoking the shell with the v option (bash v scriptname), each line of the script will be displayed just as it was typed in the script, and then executed.
Command | Option | What It Does |
---|---|---|
bash x scriptname | Echo option | Displays each line of script after variable substitutions and before execution. |
bash v scriptname | Verbose option | Displays each line of script before execution, just as you typed it. |
bash n scriptname | Noexec option | Interprets but does not execute commands. |
set x | Turns on echo | Traces execution in a script. |
set +x | Turns off echo | Turns off tracing. |
(The Script) $ cat todebug #!/bin/bash # Scriptname: todebug 1 name="Joe Shmoe" if [[ $name == "Joe Blow" ]] then printf "Hello $name\n" fi declare -i num=1 while (( num < 5 )) do let num+=1 done printf "The total is %d\n", $num (The Output) 2 bash -x todebug + name=Joe Shmoe + [[ Joe Shmoe == \J\o\e\\B\l\o\w ]] + declare -i num=1 + (( num < 5 )) + let num+=1 + (( num < 5 )) + let num+=1 + (( num < 5 )) + let num+=1 + (( num < 5 )) + let num+=1 + (( num < 5 )) + printf 'The total is %d\n,' 5 The total is 5
EXPLANATION
|
If you are writing scripts that require a number of command line options, positional parameters are not always the most efficient. For example, the UNIX ls command takes a number of command line options and arguments. (An option requires a leading dash; an argument does not.) Options can be passed to the program in several ways: ls laFi, ls i a l F, ls ia F, and so forth. If you have a script that requires arguments, positional parameters might be used to process the arguments individually, such as ls l i F . Each dash option would be stored in $1, $2, and $3, respectively. But, what if the user listed all of the options as one dash option, as in ls liF? Now the liF would all be assigned to $1 in the script. The getopts function makes it possible to process command line options and arguments in the same way they are processed by the ls program.[8] The getopts function will allow the runit program to process its arguments using any variety of combinations.
(The Command Line ) 1 $ runit x n 200 filex 2 $ runit xn200 filex 3 $ runit xy 4 $ runit yx n 30 5 $ runit n250 xy filey ( any other combination of these arguments )
EXPLANATION
|
Before getting into all the details of the runit program, we examine the line from the program where getopts is used to see how it processes the arguments.
(A Line from the Script Called "runit") while getopts :xyn: name
EXPLANATION
|
getopts Scripts. The following examples illustrate how getopts processes arguments.
(The Script) $ cat opts1 #!/bin/bash # Program: opts1 # Using getopts First try 1 while getopts xy options do 2 case $options in 3 x) echo "you entered x as an option";; y) echo "you entered y as an option";; esac done (The Command Line) 4 $ opts1 x you entered x as an option 5 $ opts1 xy you entered x as an option you entered y as an option 6 $ opts1 y you entered y as an option 7 $ opts1 b opts1: illegal option -- b 8 $ opts1 b
EXPLANATION
|
(The Script) $ cat opts2 #!/bin/bash # Program: opts2 # Using getopts Second try 1 while getopts xy options 2> /dev/null do 2 case $options in x) echo "you entered x as an option";; y) echo "you entered y as an option";; 3 \?) echo "Only -x and -y are valid options" 1>&2;; esac done (The Command Line) $ opts2 x you entered x as an option $ opts2 y you entered y as an option $ opts2 xy $ opts2 xy you entered x as an option you entered y as an option 4 $ opts2 g Only -x and -y are valid options 5 $ opts2 c Only -x and -y are valid options
EXPLANATION
|
Special getopts Variables. The getopts function provides two variables to help keep track of arguments: OPTIND and OPTARG. OPTIND is a special variable that is initialized to one and is incremented each time getopts completes processing a command line argument to the number of the next argument getopts will process. The OPTARG variable contains the value of a legal argument. See Examples 12.74 and 12.75.
(The Script) $ cat opts3 #!/bin/bash # Program: opts3 # Using getopts Third try 1 while getopts dq: options do case $options in 2 d) echo " d is a valid switch ";; 3 q) echo "The argument for -q is $OPTARG";; \?) echo "Usage:opts3 -dq filename ... " 1>&2;; esac done (The Command Line) 4 $ opts3 d d is a valid switch 5 $ opts3 -q foo The argument for -q is foo 6 $ opts3 -q Usage:opts3 -dq filename ... 7 $ opts3 e Usage:opts3 -dq filename ... 8 $ opts3 e
EXPLANATION
|
$ cat opts4 #!/bin/bash # Program: opts4 # Using getopts Fourth try 1 while getopts xyz: arguments 2>/dev/null do case $arguments in 2 x) echo "you entered -x as an option .";; y) echo "you entered -y as an option." ;; 3 z) echo "you entered -z as an option." echo "\$OPTARG is $OPTARG.";; 4 \?) echo "Usage opts4 [-xy] [-z argument]" exit 1;; esac done 5 echo " The number of arguments passed was $(( $OPTIND - 1 ))" (The Command Line) $ opts4 -xyz foo You entered -x as an option. You entered -y as an option. You entered -z as an option. $OPTARG is foo. The number of arguments passed was 2. $ opts4 -x -y -z boo You entered -x as an option. You entered -y as an option. You entered -z as an option. $OPTARG is boo. The number of arguments passed was 4. $ opts4 -d Usage: opts4 [-xy] [-z argument]
EXPLANATION
|
The eval command evaluates a command line, performs all shell substitutions, and then executes the command line. It is used when normal parsing of the command line is not flexible enough for what we want to do.
1 $ set a b c d 2 $ echo The last argument is \$$# 3 The last argument is $4 4 $ eval echo The last argument is \$$# The last argument is d 5 $ set -x $ eval echo The last argument is \$$# + eval echo the last argument is '$4' ++ echo the last argument is d The last argument is d
EXPLANATION
|
(From Shutdown Program) 1 eval '/usr/bin/id | /usr/bin/sed 's/[^a-z0-9=].*//'' 2 if [ "${uid:=0}" -ne 0 ] then 3 echo $0: Only root can run $0 exit 2 fi
EXPLANATION
|
When the shell is started using the bash command, it can take options to modify its behavior. There are two types of options: single-character options and multicharacter options. The single-character options consist of a single leading dash followed by a single character. The multicharacter options consist of two leading dashes and any number of characters. Multicharacter options must appear before single-character options. An interactive login shell normally starts up with i (start an interactive shell), s (read from standard input), and m (enable job control). See Table 12.8.
Option | Meaning |
---|---|
c string | Commands are read from string. Any arguments after string are assigned to positional parameters, starting at $0. |
D | A list of double quoted strings, preceded by a $, are printed to standard output. These strings are subject to language translation when the current locale is not C or POSIX. The n option is implied; no commands will be executed. |
i | Shell is in the interactive mode. TERM, QUIT, and INTERRUPT are ignored. |
s | Commands are read from standard input and allow the setting of positional parameters. |
r | Starts a restricted shell. |
Signals the end of options and disables further option processing. Any arguments after or are treated as filenames and arguments. | |
dump strings | Same as D. |
help | Displays a usage message for a built-in command and exits. |
login | Causes bash to be invoked as a login shell. |
noediting | When bash is running interactively, does not use the Readline library. |
noprofile | When starting up, bash does not read the initialization files: /etc/profile, ~/.bash_profile, ~/.bash_login, or ~/.profile. |
norc | For interactive shells, bash will not read the ~/.bashrc file. Turned on by default, if running shell as sh. |
posix | Changes the behavior of bash to match the POSIX 1003.2 standard, if otherwise it wouldn't. |
quiet | Displays no information at shell startup, the default. |
rcfile file | If bash is interactive, uses this intialization file instead of ~/.bashrc. |
restricted | Starts a restricted shell. |
verbose | Turns on verbose; same as v. |
version | Displays version information about this bash shell and exit. |
c string | Commands are read from string. Any arguments after string are assigned to positional parameters, starting at $0. |
D | A list of double quoted strings, preceded by a $, are printed to standard output. These strings are subject to language translation when the current locale is not C or POSIX. The n option is implied; no commands will be executed. |
i | Shell is in the interactive mode. TERM, QUIT, and INTERRUPT are ignored. |
s | Commands are read from standard input and allows the setting of positional parameters. |
r | Starts a restricted shell. |
Signals the end of options and disables further option processing. Any arguments after or are treated as filenames and arguments. | |
login | Causes bash to be invoked as a login shell. |
nobraceexpansion | Curly brace expansion is turned off. |
nolineediting | When bash is running interactively, does not use the Readline library. |
noprofile | When starting up, bash does not read the initialization files: /etc/profile, ~/.bash_profile, ~/.bash_login, or ~/.profile. |
posix | Changes the behavior of bash to match the POSIX standard, if otherwise it wouldn't. |
quiet | Displays no information at shell startup, the default. |
rcfile file | If bash is interactive, uses this intialization file instead of ~/.bashrc. |
verbose | Turns on verbose; same as v. |
version | Displays version information about this bash shell and exit. |
The set command can be used to turn shell options on and off, as well as for handling command line arguments. To turn an option on, the dash ( ) is prepended to the option; to turn an option off, the plus sign (+) is prepended to the option. See Table 12.10 for a list of set options.
1 $ set -f 2 $ echo * * 3 $ echo ?? ?? 4 $ set +f
EXPLANATION
|
Name of Option | Shortcut Switch | What It Does |
---|---|---|
allexport | a | Automatically marks new or modified variables for export from the time the option is set, until unset. |
braceexpand | B | Enables brace expansion, and is a default setting.[a] |
emacs | For command line editing, uses the emacs built-in editor, and is a default setting. | |
errexit | e | If a command returns a nonzero exit status (fails), exits. Not set when reading initialization files. |
histexpand | H | Enables ! and !! when performing history substitution, and is a default setting.[a] |
history | Enables command line history; on by default.[a] | |
ignoreeof | Disables EOF (Control-D) from exiting a shell; must type exit. Same as setting shell variable, IGNOREEOF=10. | |
keyword | k | Places keyword arguments in the environment for a command.[a] |
interactive-comments | For interactive shells, a leading # is used to comment out any text remaining on the line. | |
monitor | m | Allows job control. |
noclobber | C | Protects files from being overwritten when redirection is used. |
noexec | n | Reads commands, but does not execute them. Used to check the syntax of scripts. Not on when running interactively. |
noglob | d | Disables pathname expansion; i.e., turns off wildcards. |
notify | b | Notifies user when background job finishes |
nounset | u | Displays an error when expanding a variable that has not been set. |
onecmd | t | Exits after reading and executing one command.[a] |
physical | P | If set, does not follow symbolic links when typing cd or pwd. The physical directory is used instead. |
posix | Shell behavior is changed if the default operation doesn't match the POSIX standard. | |
privileged | p | When set, the shell does not read the .profile or ENV file and shell functions are not inherited from the environment; automatically set for setuid scripts. |
posix | Changes the default behavior to POSIX 1003.2. | |
verbose | v | Turns on the verbose mode for debugging. |
vi | For command line editing, uses the vi built-in editor. | |
xtrace | x | Turns on the echo mode for debugging. |
[a] Option applies only to versions of bash 2.x.
The shopt (bash 2.x) command can also be used to turn shell options on and off.
Option | Meaning |
---|---|
cdable_vars | If an argument to the cd built-in command is not a directory, it is assumed to be the name of a variable whose value is the directory to change to. |
cdspell | Corrects minor errors in the spelling of a directory name in a cd command. The errors checked for are transposed characters, a missing character, and a character too many. If a correction is found, the corrected path is printed, and the command proceeds. Only used by interactive shells. |
checkhash | Bash checks that a command found in the hash table exists before trying to execute it. If a hashed command no longer exists, a normal path search is performed. |
checkwinsize | Bash checks the window size after each command and, if necessary, updates the values of LINES and COLUMNS. |
cmdhist | Bash attempts to save all lines of a multiple-line command in the same history entry. This allows easy re-editing of multiline commands. |
dotglob | Bash includes filenames beginning with a dot (.) in the results of filename expansion. |
execfail | A noninteractive shell will not exit if it cannot execute the file specified as an argument to the exec built-in command. An interactive shell does not exit if exec fails. |
expand_aliases | Aliases are expanded. Enabled by default. |
extglob | The extended pattern matching features (regular expression metacharacters derived from Korn shell for filename expansion) are enabled. |
histappend | The history list is appended to the file named by the value of the HISTFILE variable when the shell exits, rather than overwriting the file. |
histreedit | If readline is being used, a user is given the opportunity to re-edit a failed history substitution. |
histverify | If set, and readline is being used, the results of history substitution are not immediately passed to the shell parser. Instead, the resulting line is loaded into the readline editing buffer, allowing further modification. |
hostcomplete | If set, and readline is being used, bash will attempt to perform hostname completion when a word containing an @ is being completed. Enabled by default. |
huponexit | If set, bash will send SIGHUP (hangup signal) to all jobs when an interactive login shell exits. |
interactive_comments | Allows a word beginning with # to cause that word and all remaining characters on that line to be ignored in an interactive shell. Enabled by default. |
lithist | If enabled, and the cmdhist option is enabled, multiline commands are saved to the history with embedded newlines rather than using semicolon separators where possible. |
mailwarn | If set, and a file that bash is checking for mail has been accessed since the last time it was checked, the message The mail in mailfile has been read is displayed. |
nocaseglob | If set, bash matches filenames in a case-insensitive fashion when performing filename expansion. |
nullglob | If set, bash allows filename patterns that match no files to expand to a null string, rather than themselves. |
promptvars | If set, prompt strings undergo variable and parameter expansion after being expanded. Enabled by default. |
restricted_shell | The shell sets this option if it is started in restricted mode. The value may not be changed. This is not reset when the startup files are executed, allowing the startup files to discover whether or not a shell is restricted. |
shift_verbose | If this is set, the shift built-in prints an error message when the shift count exceeds the number of positional parameters. |
sourcepath | If set, the source built-in uses the value of PATH to find the directory containing the file supplied as an argument. Enabled by default. |
source | A synonym for dot (.). |
The shell has a number of commands that are built-in to its source code. Because the commands are built-in, the shell doesn't have to locate them on disk, making execution much faster. The help feature provided with bash give you online help for any built-in command. The built-in commands are listed in Table 12.12.
Command | What It Does |
---|---|
. | Executes program in context of current process; same as source. |
. file | The dot command reads and executes command from file. |
: | Do-nothing command; returns 0 exit status. |
alias | Lists and creates "nicknames" for existing commands. |
bg | Puts a job in the background. |
bind | Displays current key and function bindings, or binds keys to a readline function or macro.[a] |
break | Breaks out of the innermost loop. |
break [n] | See "The break Command" 780. |
builtin [sh builtin [args]] | Runs a shell built-in, passing it args, and returning 0 exit status. Useful if a function and built-in have the same name.[a] |
cd [arg] | Changes the directory to home if no arg or to value of arg. |
command command [arg] | Runs a command even if a function has the same name; i.e., bypasses function lookup.[a] |
continue [n] | See "The continue Command" on page 781. |
declare [var] | Displays all variables or declares variables with optional attributes.[a] |
dirs | Displays a list of currently remembered directories resulting from pushd. |
disown | Removes an active job from the job table. |
echo [args] | Displays args terminated with a newline. |
enable | Enables and disables shell built-in commands.[a] |
eval [args] | Reads args as input to the shell and executes the resulting command(s). |
exec command | Runs command in place of this shell. |
exit [n] | Exits the shell with status n. |
export [var] | Makes var known to subshells. |
fc | History's fix command for editing history commands. |
fg | Puts background job into foreground. |
getopts | Parses and processes command line options. |
hash | Controls the internal hash table for quicker searches for commands. |
help [command] | Displays helpful info about built-in commands and, if command is specified, detailed help about that built-in command.[a] |
history | Displays the history list with line numbers. |
jobs | Lists jobs put in the background. |
kill [ signal process] | Sends the signal to the PID number or job number of the process. Type kill l for a list of signals. |
let | Used for evaluating arithmetic expressions and assigning results of arithmetic calculations to variables. |
local | Used in functions to restrict the scope of variables to the function. |
logout | Exits the login shell. |
popd | Removes entries from the directory stack. |
pushd | Adds entries to the directory stack. |
pwd | Prints present working directory. |
read [var] | Reads line from standard input into variable var. |
readonly [var] | Makes variable var read-only. Cannot be reset. |
return [n] | Returns from a function where n is the exit value given to the return. |
set | Sets options and positional parameters. See Table 12.2. |
shift [n] | Shifts positional parameters to the left n times. |
stop pid | Halts execution of the process number PID. |
suspend | Stops execution of the current shell (but not if a login shell). |
test | Checks file types and evaluates conditional expressions. |
times | Prints accumulated user and system times for processes run from this shell. |
trap [arg] [n] | When shell receives signal n ( 0, 1, 2, or 15), executes arg. |
type [command] | Prints the type of command; e.g., pwd is a built-in shell command. |
typeset | Same as declare. Sets variables and gives them attributes. |
ulimit | Diplays and sets process resource limits. |
umask [octal digits] | Sets user file creation mode mask for owner, group, and others. |
unalias | Unsets aliases. |
unset [name] | Unset value of variable or function. |
wait [pid#n] | Waits for background process with PID number n and reports termination status. |
[a] Option applies to bash 2.x. and later.
1: | Write a script called greetme that will do the following:
|
2: | Make sure your script is executable. chmod +x greetme |
3: | What was the first line of your script? Why do you need this line? |
1: | Write a script called rename that will take two arguments: the first argument is the name of the original file and the second argument is the new name for the file. If the user does not provide two arguments, a usage message will appear on the screen and the script will exit. Here is an example of how the script works: $ rename Usage: rename oldfilename newfilename $ $ rename file1 file2 file1 has been renamed file2 Here is a listing of the directory: a file2 b file.bak |
2: | The following find command (SunOS) will list all files in the root partition that are larger than 100K and that have been modified in the last week. (Check your man pages for the correct find syntax on your system.) find / xdev mtime 7 size +200 print |
3: | Write a script called bigfiles that will take two arguments: one will be the mtime and one the size value. An appropriate error message will be sent to stderr if the user does not provide two arguments. |
4: | If you have time, write a script called vib that creates backup files for vi. The backup files will have the extension .bak appended to the original name. |
1: | Write a script called nosy that will do the following:
|
2: | Create a text file called datafile (unless this file has already been provided for you). Each entry consists of fields separated by colons. The fields are as follows:
|
3: | Create a script called lookup that will do the following:
|
4: | Try the x and v options for debugging your script. How did you use these commands? How do they differ? |
1: | Write a script called checking that will do the following:
|
2: | In the lookup script, ask the user if he or she would like to add an entry to the datafile. If the answer is yes or y:
|
1: | Rewrite checking. After checking whether the named user is in the /etc/passwd file, the program will check to see if the user is logged on. If so, the program will print all the processes that are running; otherwise it will tell the user <user> is not logged on. |
2: | Use the let command to evaluate a set of grades. The script will ask the user for his or her numeric grade on an examination. ( Use declare i ). The script will test that the grade is within the allowable range between 0 and 100. If not, the program will exit. If the grade is within the range, the user's letter grade will be displayed, e.g., You received an A. Excellent! The range is as follows:
|
3: | The lookup script depends on the datafile in order to run. In the lookup script, check to see if the datafile exists and if it is readable and writeable. Add a menu to the lookup script to resemble the following:
You already have the Add entry part of the script written. The Add entry routine should now include code that will check to see if the name is already in the datafile and if it is, tell the user so. If the name is not there, add the new entry. Now write the code for the Delete entry, View entry, and Exit functions. The Delete part of the script should first check to see if the entry exists before trying to remove it. If it does, notify the user; otherwise, remove the entry and tell the user you removed it. On exit, make sure that you use a digit to represent the appropriate exit status. How do you check the exit status from the command line? |
1: | The ps command is different on BSD (Berkeley UNIX) and System 5 (AT&T UNIX). UNIX uses the BSD options to ps. On System 5, the command to list all processes is ps ef On UNIX, the command is ps aux Write a program called systype that will check for a number of different system types. The cases to test for will be
Solaris, HP UX, SCO, and IRIX are AT&T-type systems. The rest are BSDish. The version of UNIX you are using will be printed to stdout. The system name can be found with the uname s command or from the /etc/motd file. |
2: | Write a script called timegreet that will do the following:
|
Select one of the following:
1: | Write a program called mchecker to check for new mail and write a message to the screen if new mail has arrived.
The size of a file can be found by looking at the output fromls l, wc c or from the find command. |
2: | Write a script that will do the following:
|
3: | Write a program called dusage that will mail a list of users, one at a time, a listing of the number of blocks they are currently using. The list of users will be in a file called potential_hogs. One of the users listed in the potential_hogs file will be admin.
|
1: | Rewrite the systype program as a function that returns the name of the system. Use this function to determine what options you will use with the ps command in the checking program. |
2: | The ps command to list all processes on AT&T UNIX is ps ef |
3: | On UNIX/BSD UNIX, the command is ps aux or ps aux[9] |
4: | Write a function called cleanup that will remove all temporary files and exit the script. If the interrupt or hangup signal is sent while the program is running, the trap command will call the cleanup function. |
5: | Use a here document to add a new menu item to the lookup script to resemble the following:
Write a function to handle each of the items in the menu. After the user has selected a valid entry, and the function has completed, ask if the user would like to see the menu again. If an invalid entry is entered, the program should print
and the menu will be redisplayed. |
6: | Create a submenu under View entry in the lookup script. The user will be asked if he or she would like to view specific information for a selected individual:
|
7: | Use the trap command in a script to perform a cleanup operation if the interrupt signal is sent while the program is running. |
[1] When bash starts interactively, if the norc or norc option is given, the BASH_ENV or ENV file will not be read.
[2] Options a, e, and p are available only in bash versions 2.x.
[3] Remember, without arguments, the set command displays all the variables that have been set for this shell, local and exported. With options, the set command turns on and off shell control options such as x and v.
[4] If the continue command is given a number higher than the number of loops, the loop exits.
[5] Only on bash versions 2.x.
[6] Bolsky, Morris I. and Korn, David G., The New KornShell Command and Programming Language (Englewood Cliffs, NJ: Prentice Hall PTR, 1995), p. 327.
[7] SIGKILL, number 9, often called a "sure kill," is not trapable.
[8] See the UNIX manual pages (Section 3) for the C library function getopt.
[9] Using the leading dash with UNIX will produce a warning. See the man page.
![]() | CONTENTS | ![]() |