In the past few months, this column has explored the intricacies of Linux's bash shell. Since we're on a roll, we're going to continue this month by looking at the shell's logical operators and conditional statements -- the ands and ifs. Along the way, we'll use conditional statements to construct a simple archive command that can help you use every available byte of disk storage space.
In the past few months, this column has explored the intricacies of Linux’s bash shell. Since we’re on a roll, we’re going to continue this month by looking at the shell’s logical operators and conditional statements — the ands and ifs. Along the way, we’ll use conditional statements to construct a simple archive command that can help you use every available byte of disk storage space.
Getting the Size of a File
If you’re a PC veteran, you may recall the days of the 5 MB hard drive. By today’s standards, these drives were pricey, pokey, and puny. Today, even 80 GB drives are common. However, even your 80 GB drives are probably packed to the gills.
One way to better use your available disk space is to compress many of your large text files. The gzip utility is ideally suited to this task. The problem is merely one of identifying the proper files to compress.
Since there’s little to be gained by compressing a small file, candidates for compression should be large files. And, since uncompressing a file involves a small inconvenience, it’s best to compress only files that aren’t in regular use. We could use the find command, which was explained in the April 2001 column (check it out online at http://www.linux-mag.com/2001-04/newbies_01.html). However, let’s see if we can do the job the hard way, learning something and gaining some flexibility along the way.
First, let’s create a command that displays the size of the file specified as the command’s argument. Here’s a suitable command, which we’ll call filesize:
The du displays the size of the file whose name is specified as its argument. The -k argument causes the size to be given in kilobytes. The $1 argument is replaced by the first argument of the filesize command; that is, if you execute the command:
The command actually executed is:
du-k/var/log/messages| cut-f 1
The | operator (the pipe redirection operator) sends the output of the du command to the cut command. The cut-f1 command displays only the first word of each line of its input, deleting any other words. So, when the du command produces the output:
the cut command displays only the file size, 243 KB.
The if Statement
Now that we’re able to obtain the size of a file, we’re ready to compress only large files. To do so, we’ll need to execute the gzip command conditionally; that is, only for large files. The Linux shell includes two statements that could serve our purpose — if and case (which we’ll look at next month). Let’s take a look at the if statement, which has the general form:
If you’ve programmed in any language from Algol to Zeta, you’ll likely recognize how the if statement works without explanation. If not, don’t fret; the if is a simple beast.
Here’s how it works. First, the if statement executes the command that follows the keyword if. Like every Linux command, that command will return a numeric value called the exit status. The exit status is available in the shell variable $?. What matters is the value of the exit status. If the exit status is 0, the result is deemed true; otherwise, it’s deemed false. The value 0 (true) is generally associated with successful completion of a command; other values generally indicate the nature of the error that occurred. In any case, if the exit status has the value 0 (true), the if statement executes the commands following the keyword then; otherwise, it doesn’t.
One trick to bear in mind: the layout of the if statement is important. The keywords if, then, and fi are recognized only when they appear at the beginning of a line. So, when writing an if statement, you should follow the form given.
As an example, suppose you issue the following command:
if rm badfile
echo It worked.
The command will first attempt to delete the file named badfile and will set the exit status based on the result. If the deletion was successful, the echo statement will be executed; otherwise, it will not.
Testing, One, Two, Three
We can use the if statement to help us identify the files that have abs of flab rather than abs of steel. But, to do so, we’ll need the help of the test command, which we’ll use to set the exit status. As it happens, that’s the sole purpose of the test command, which is almost always used in combination with the if or if-else statements.
The test command has the general form:
where expression controls the operation of the test command, identifying the sort of test to be performed. The test sets the exit status based on the result of the test. The test command is used so often that the Linux shell supports a special way of issuing it. The construct:
is equivalent to:
Table One: Expressions Used with the test Command
|\( expression \) ||expression is true|
|! expression||expression is false|
|expression1 -a expression2||both expression1 and expression2 are true|
|expression1 -o expression2||either expression1 or expression2 is true|
|file1 -ef file2||file1 and file2 have the same device and i-node numbers|
|file1 -nt file2||the modification time of file1 is later than that of file2|
|file1 -ot file2||file1 is older than file2|
|integer1 -eq integer2||integer1 is equal to integer2|
|integer1 -ge integer2||integer1 is greater than or equal to integer2|
|integer1 -gt integer2||integer1 is greater than integer2|
|integer1 -le integer2||integer1 is less than or equal to integer2|
|integer1 -lt integer2||integer1 is less than integer2|
|integer1 -ne integer2||integer1 is not equal to integer2|
|[-n] stringthe length of ||string is non-zero|
|string1 != string2||the strings are not equal|
|string1 = string2||the strings are equal|
|-b file||file exists and is block special|
|-c file||file exists and is character special|
|-d file||file exists and is a directory|
|-e file||file exists|
|-f file||file exists and is a regular file|
|-g file||file exists and is set-group-ID|
|-G file||file exists and is owned by the effective group ID|
|-k file||file exists and has its sticky bit set|
|-L file||file exists and is a symbolic link|
|-O file||file exists and is owned by the effective user ID|
|-p file||file exists and is a named pipe|
|-r file||file exists and is readable|
|-s file||file exists and has a size greater than zero|
|-S file||file exists and is a socket|
|-t [ fd ]|| the specified file descriptor fd ( stdout by default) is opened on a terminal|
|-u file||file exists and its set-user-ID bit is set|
|-w file||file exists and is writable|
|-x file||file exists and is executable|
|-z string||the length of string is zero|
The test command supports a variety of tests, as shown in Table One. An option in forming test expressions lets you substitute -l string in place of any integer test expression. This special expression returns the length of the specified string. As an example, here’s a typical use of the test command:
if [ -e Badfile ]
echo Badfile exists.
echo Badfile does not exist.
This command will display the text Badfile exists if the file named Badfile exists and the text Badfile does not exist otherwise.
Files with Abs of Flab
Okay, let’s now use the if and test commands together to check out our flabby files. Recall that backquotes cause the shell to execute the enclosed command and substitute the result of command execute for the quoted string. Therefore, the value of the shell expression:
will be the size, in kilobytes, of the file whose name is given by the shell argument, $1.
Let’s use this expression in combination with the test command:
if test `du -k $1 | cut -f 1` -ge 1000
echo The file $1 has abs of flab.
Place this script in a file named myarchive and give yourself execute access by issuing the command:
Test the script by using it to weigh in a file; for example, you can use the following:
If the file named thefile has a size of at least 1000 KB, the script should report that the file has “abs of flab.”
As explained, we’re interested in flabby text files, not merely flabby files. So we need to further qualify candidates for compression. The file command can help us here. If you were to issue:
file would report the name of the candidate file, followed by a colon and space, and the type of the file. If the candidate file is a text file, the file will report the type “ASCII text.”
The following is a shell expression that reports the type of the file which has a name contained in the shell variable $1:
`file $1 | cut -f 2 -d ‘ ‘`
The cut command used in the above example is somewhat more involved than those previously used. The additional arguments tell the command to use space as a delimiter. This is necessary because file separates its output using spaces, whereas du separates its output using a TAB character, which is also the default delimiter employed by cut.
Here’s how to write an if statement that tests the type of a file:
if test `file $1 | cut -f 2 -d ‘ ‘` = ASCII
echo File $1 is an ASCII file.
Place this script in a file named myarchive2 and give yourself execute access to the file by issuing the following command:
chmod u+x myarchive2
Test the script by using it to weigh in a file; for example:
If the file named thefile is a text file, the script should report that the file “is an ASCII file.”
Integration: The Ifs and the Ands
We can now write an if statement that tests whether a file is flabby, and we can write an if statement that tests whether a file is a text file. All that remains is to write an if statement that will test both conditions.
To do so, we can use the test command’s -a expression, which denotes the conjunction AND. The -a expression joins two subexpressions, both of which must be true for the expression as a whole to be considered true. An example of this conjunction is shown in Listing One.
Notice the script uses a backslash to continue the first line, which would otherwise be too long to be conveniently read and understood. Place this script in a file named myarchive3 and give yourself execute access to the file by issuing the command:
Notice that the revised script doesn’t merely report the characteristics of the file whose name is specified as an argument. It runs the gzip command to compress the file. This command replaces the file with a new, compressed file having as the first part of its name the name of the original file and the file extension .gz. To uncompress a file compressed using gzip, issue the following command:
where file.gz is the name of the compressed file.
Test the myarchive3 script by using it to weigh in a file; for example:
If the file named thefile is a big text file, the script should report that the file “is a flabby text file” and compress it. Don’t attempt to use this script to compress files that are used by programs (such as configuration files). If you accidentally compress a file that really should not be compressed, simply use the gunzip command to uncompress the file.
On the Case
You’ve now seen the ifs and the ands. But, as the saying goes, that’s not all. Next month’s column will cover the case statement, a still more powerful cousin of the if statement.
Listing One: Joining if Statements with -a
if test `du -k $1 | cut -f 1` -ge 1000 \
-a `file $1 | cut -f 2 -d ‘ ‘` = ASCII
echo File $1 is a flabby text file.
Bill McCarty is an associate professor at Azusa Pacific University. He can be reached at firstname.lastname@example.org.