The Complete Library
The Complete Library
Listing 21.1 contains a complete listing of the shell library, libTYSP.sh , discussed in this chapter. The line numbers are present for your reference and should not be entered into a script library file that you plan to use.
Listing 21.1--Complete Listing of the Shell Library libTYSP.sh
1 #!/bin/sh
4 # Name: printERROR
5 # Desc: prints an message to STDERR
6 # Args: $@ -> message to print
9 printERROR() {
10 echo "ERROR:" $@ >&2
14 # Name: printWARNING
15 # Desc: prints an message to STDERR
16 # Args: $@ -> message to print
20 echo "WARNING:" $@ >&2
24 # Name: printUSAGE
25 # Desc: prints a USAGE message and then exits
26 # Args: $@ -> message to print
30 echo "USAGE:" $@
35 # Name: promptYESNO
36 # Desc: ask a yes/no question
37 # Args: $1 -> The prompt
38 # $2 -> The default answer (optional)
39 # Vars: YESNO -> set to the users response
40 # y for yes, n for no
45 if [ $# -lt 1 ] ; then
46 printERROR "Insufficient Arguments."
50 DEF_ARG=""
51 YESNO=""
53 case "$2" in
54 [yY]|[yY][eE][sS])
55 DEF_ARG=y ;;
56 [nN]|[nN][oO])
57 DEF_ARG=n ;;
58 esac
60 while :
63 printf "$1 (y/n)? "
64
65 if [ -n "$DEF_ARG" ] ; then
66 printf "[$DEF_ARG] "
67 fi
68
69 read YESNO
70
71 if [ -z "$YESNO" ] ; then
72 YESNO="$DEF_ARG"
73 fi
74
75 case "$YESNO" in
76 [yY]|[yY][eE][sS])
77 YESNO=y ; break ;;
78 [nN]|[nN][oO])
79 YESNO=n ; break ;;
86 export YESNO
87 unset DEF_ARG
92 # Name: promptRESPONSE
93 # Desc: ask a question
94 # Args: $1 -> The prompt
95 # $2 -> The default answer (optional)
96 # Vars: RESPONSE -> set to the users response
97 ################################################
98
99 promptRESPONSE() { 100 101 if [ $# -lt 1 ] ; then 102 printERROR "Insufficient Arguments." 103 return 1 104 fi 105 106 RESPONSE="" 107 DEF_ARG="$2" 108 109 while : 110 do 111 printf "$1 ? " 112 if [ -n "$DEF_ARG" ] ; then 113 printf "[$DEF_ARG] " 114 fi
117 118 if [ -n "$RESPONSE" ] ; then 119 break 120 elif [ -z "$RESPONSE" -a -n "$DEF_ARG" ] ; then 121 RESPONSE="$DEF_ARG" 122 break 123 fi 124 done 125 126 export RESPONSE 127 unset DEF_ARG 128 return 0 129 } 130 131 ################################################ 132 # Name: getSpaceFree 133 # Desc: output the space avail for a directory 134 # Args: $1 -> The directory to check 135 ################################################ 136 137 getSpaceFree() { 138 139 if [ $# -lt 1 ] ; then 140 printERROR "Insufficient Arguments." 141 return 1 142 fi 143 144 df -k "$1" | awk 'NR != 1 { print $4 ; }' 145 } 146 147 ################################################ 148 # Name: getSpaceUsed 149 # Desc: output the space used for a directory 150 # Args: $1 -> The directory to check 151 ################################################ 152 153 getSpaceUsed() { 154 155 if [ $# -lt 1 ] ; then 156 printERROR "Insufficient Arguments." 157 return 1 158 fi 159 160 if [ ! -d "$1" ] ; then 161 printERROR "$1 is not a directory." 162 return 1 163 fi 164 165 du -sk "$1" | awk '{ print $1 ; }' 166 } 167 168 ################################################
171 # Args: $1 -> the command name to look for 172 ################################################ 173 174 getPID() { 175 176 if [ $# -lt 1 ] ; then 177 printERROR "Insufficient Arguments." 178 return 1 179 fi 180 181 PSOPTS="-ef" 182 183 /bin/ps $PSOPTS | grep "$1" | grep -v grep | awk '{ print $2;
}' 184 } 185 186 ################################################ 187 # Name: getUID 188 # Desc: outputs a numeric user id 189 # Args: $1 -> a user name (optional) 190 ################################################ 191 192 getUID() { 193 id $1 | sed -e 's/(.*$//' -e 's/^uid=//' 194 } 195
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter:
Creating a Library of Functions Questions Useful Functions
Terms
Summary
Previous Section Next Section © Copyright Macmillan Computer Publishing. All rights reserved.
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter:
Creating a Library of Functions Questions
Useful Functions
Terms
Summary
Previous Section Next Section
Summary
In this chapter, I presented a library of shell functions that can be used in your shell scripts to handle many common tasks. By using and improving these implementations, you can avoid having to reinvent the wheel when faced with a particular problem.
Some of the problems that I addressed are
Displaying standardized error, warning, and usage messages
Prompting for a yes or no response
Prompting for a general response
Checking disk space
Getting the process ID of a command using its name
Getting the numeric user ID of a user In addition to these tasks, I have covered many other useful functions throughout this book. By using these
functions, you can concentrate on developing scripts to solve complicated problems without worrying about the basics.
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter:
Creating a Library of Functions Questions
Useful Functions
Terms
Summary
Previous Section Next Section © Copyright Macmillan Computer Publishing. All rights reserved.
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter:
Creating a Library of Functions Questions Useful Functions
Terms
Summary
Previous Section Next Section
Questions
1. Write a function called toLower that converts its arguments to all lowercase and displays the converted string to STDOUT . You don't have to worry about checking the number of arguments.
(HINT: Use the tr command.)
2. Write a function called toUpper that converts its arguments to all uppercase and displays the converted string to STDOUT . You don't have to worry about checking the number of arguments.
(HINT: Use the tr command.)
3. Write a function called isSpaceAvailable to check whether a directory contains a certain amount of disk space.
Your function should accept two arguments. The first one indicates the directory to check, and the second one indicates the amount of space to check. An error should be reported if both arguments are not given. Your function should validate that the first argument is a directory.
If sufficient space is present, your function should return 0; otherwise, it should return 1. This enables us to use it as follows:
if isSpaceAvailable /usr/local 20000 ; then : # perform some action fi
(HINT: Use the function getSpaceFree .)
4. Modify your isSpaceAvailable function to accept an optional third argument that specifies the units of the amount space to check.
The default should remain in kilobytes, but you should support m or mb indicating megabytes and g or gb indicating gigabytes. If some other units are given, assume that the user meant kilobytes.
The following conversion factors apply to this problem: 1MB equals 1024KB, and 1GB equals to 1024MB.
(HINT: Use the bc command.)
(HINT: Use the getUID function.)
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter: Creating a Library of Functions Questions
Useful Functions
Terms
Summary
Previous Section Next Section © Copyright Macmillan Computer Publishing. All rights reserved.
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter:
Creating a Library of Functions Questions Useful Functions
Terms
Summary
Previous Section Next Section
Terms
Library--A file that contains only functions is called a library. Usually libraries contain no main code.
Main Code --Main code consists of all the commands in a shell script that are not contained within functions.
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 21: Problem Solving with Functions
Previous Chapter Next Chapter Sections in this Chapter:
Creating a Library of Functions Questions Useful Functions
Terms
Summary
Previous Section Next Section © Copyright Macmillan Computer Publishing. All rights reserved.
Sams Teach Yourself Shell Programming in 24 Hours
Contents Index Hour 22: Problem Solving with Shell Scripts
Previous Chapter Next Chapter Sections in this Chapter:
Moving Directories
Questions
Maintaining an Address Book Terms
Summary
Previous Section Next Section
Hour 22 Problem Solving with Shell Scripts
In Chapter 21, "Problem Solving with Functions,"
I showed you several useful functions that you can use in your shell scripts. In this chapter, I will present two shell scripts that demonstrate how you can use shell
scripts to solve everyday problems. These scripts illustrate using the tools I covered in previous chapters to create new tools that you can reuse.
For each script I will first describe the motivations for its development, followed by some design issues. Then I will present the script in full. I will conclude the discussion of scripts by highlighting the script's flow and error checking.
The two tasks that I will look at are
Moving directories
Maintaining an Address Book
Moving Directories
Using tar A Walkthrough of mvdir.sh mvdir.sh Examples
I noted that the mv command could not be used to move directories across file systems. A file system can be thought of as a hard drive or hard drive partition.
In Chapter 4, "Working with Directories,"
The mv command works fine when you want to move a directory between different locations on the same file system (hard drive), but it doesn't work well when you want to move a file across file systems. Depending on your version of mv , an error message could be generated when you try to do this.
For example, consider this directory: $ ls -F /tmp/ch22
ch22-01.doc ch22.doc@ If you use the mv command to move this directory in the directory /home/ranga on a different file system,
an error message similar to the following is generated:
Some UNIX versions implement a workaround inside mv that executes the following commands: $ rm -rf destination
$ cp -r source destination $ rm -rf source
Here source and destination are directories. The main problem with this strategy is that links in the source directory are not always copied correctly. Most
of the time, the file that the link points to is copied instead of the link itself. In the case of the directory /tmp/ch22 , you would end up with two copies of the file ch22-01.doc , which is not desirable.
In addition to this, there are two other minor problems with using cp :
Some versions of the cp command do not copy a file's owner and group. With these versions of cp , the copied file has a different owner and group than the original.
Some versions of cp do not copy a file's permissions correctly. With such a version of cp , the copied file might have different permissions than the original.
Using tar
The workaround for these problems is to use the tar ( tar as in tape archive ) command to copy directories. This command creates an archive or tar file that contains files and directories. A tar file is similar to a zip file, except that its contents are not compressed. In addition, a tar file stores the file permission along with group and owner information for the files it contains. Thus by using tar , your copies automatically end up with the correct file attributes.
By using tar you can move directories using the following procedure:
1. Make a tar file of the source directory.
2. Change to the destination directory.
3. Extract the source directory in the destination directory.
4. Remove the source directory. Notice that your procedure does not include deleting the tar file of the source directory. Normally when you use the tar command, a tar file is created on your hard drive. If you use this behavior in your script, you need to worry about cleaning up the tar file whenever an error occurs. This adds a large amount of complexity to your script. To avoid all that unnecessary complexity, you can use a special feature of the tar command to avoid creating a tar file.
The tar command can create archives and write them to STDOUT instead of a file. It can also read archives from STDIN instead of from a file. By using a pipe character ( | ), you can connect a tar command that creates a tar file with one that extracts a tar file, thus avoiding the creation of an intermediate tar file.
tar -cpf - source Here source is the pathname of a directory. The options specified to tar tell it to create a tar file, whereas
the - indicates that the tar file it creates should be written to STDOUT . To extract a tar file from STDIN , use the command: tar -xpf - Here the options specified to tar indicate that it should extract a tar file, whereas the - indicates that the tar
file should be read from STDIN . Because you need to extract the tar file in the correct directory, the final command you use is tar -cpf - source | ( cd destination ; tar -xpf - ) Here source and destination are directories. This single command takes care of the first three steps
involved in moving a directory. The rest of your script performs error checking and ensures that sensible values for source and destination are used.
mvdir.sh
The script mvdir.sh is given in Listing 22.1 (the line numbers are provided for your reference).
Listing 22.1 Complete Listing of the mvdir.sh Script
1 #!/bin/sh
2 # Name: mvdir
3 # Desc: Move directories across file systems
4 # Args: $1 -> src dir
5 # $2 -> dest dir
7 PATH=/bin:/usr/bin ; export PATH
9 # function to print errors and exit
11 printERROR() { echo "ERROR: $@." >&2 ; exit 1; }
13 # function to print usage message and exit
15 printUSAGE() { echo "USAGE: ´/bin/basename $0´ $@." >&2 ; exit 1; }
17 # check whether sufficient args are given
19 if [ $# -lt 2 ] ; then printUSAGE "[src] [dest]" ; fi
21 # check whether the source directory exists
24 printERROR "The source $1 is not a directory, or does not exist"
25 fi
27 # split up the source dir into its name and its parent's
28 # name for easier processing later on
30 SRCDIR_PARENT="´/usr/bin/dirname $1´"
31 SRCDIR_CHILD="´/bin/basename $1´"
33 # if dirname returns a relative dir we will be confused
34 # after cd'ing later on. So reset it to the full path.
36 SRCDIR_PARENT="´(cd $SRCDIR_PARENT ; pwd ; )´"
38 # check whether the destination exits
40 if [ -d "$2" ] ; then
42 DESTDIR=´( cd "$2" ; pwd ; )´
46 # if the destination doesn't exist then assume the
47 # destination is the new name for the directory
49 DESTDIR="´/usr/bin/dirname $2´"
50 NEWNAME="´/bin/basename $2´"
52 # if dirname returns a relative dir we will be confused
53 # after cd'ing later on. So reset it to the full path.
55 DESTDIR=´(cd $DESTDIR ; pwd ; )´
57 # if the parent of the destination doesn't exist,
58 # we're in trouble. Tell the user and exit.
60 if [ ! -d "$DESTDIR" ] ; then
61 printERROR "A parent of the destination directory $2 does not exist"
66 # try and cd to the parent src directory
68 cd "$SRCDIR_PARENT" > /dev/null 2>&1
69 if [ $? -ne 0 ] ; then
70 printERROR "Could not cd to $SRCDIR_PARENT"
71 fi
73 # use tar to copy the source dir to the destination
77 if [ $? -ne 0 ] ; then
78 printERROR "Unable to successfully move $1 to $2"
79 fi
81 # if a rename of the copy is requested
83 if [ -n "$NEWNAME" ] ; then
85 # try and change to the destination directory
87 cd "$DESTDIR" > /dev/null 2>&1
88 if [ $? -ne 0 ] ; then
89 printERROR "Could not cd to $DESTDIR"
90 fi
92 # try and rename the copy
94 /bin/mv "$SRCDIR_CHILD" "$NEWNAME" > /dev/null 2>&1
95 if [ $? -ne 0 ] ; then
96 printERROR "Could not rename $1 to $2"
97 fi
99 # return to the original directory 100 101 cd "$SRCDIR_PARENT" > /dev/null 2>&1 102 if [ $? -ne 0 ] ; then 103 printERROR "Could not cd to $SRCDIR_PARENT" 104 fi 105 fi 106 107 # try and remove the original 108 109 if [ -d "$SRCDIR_CHILD" ] ; then 110 /bin/rm -r "$SRCDIR_CHILD" > /dev/null 2>&1 111 if [ $? -ne 0 ] ; then 112 printERROR "Could not remove $1" 113 fi 114 fi 115 116 exit 0