Decision Making
Decision Making
Note that you can use the ! sign before condition to negate it. That is, exit with 0 if the condition
is not met. For example ! [[ "b" > "a" ]] is the same as [[ "b" < "a" ]]
LAB: Further enhancing highest.sh
So far we have tested that a string representing the filename is passed to the script, and that
this string represents the path to an actual, existing file. But if that file was empty? You may
want to print some friendly message to the user instead of just a blank line. This could be done
as follows:
filename=${1:?Please enter a filename}
count=${2:-5}
if ls $1 > /dev/null 2>&1 && [[ -n $(cat $filename) ]]; then
sort -n $filename | tail -$count
else
echo "Invalid filename or file is empty"
exit 1
fi
Here we added an extra condition, and using the && operator, we are forcing the script to
process the file only if the two conditions are met. The second condition ensures that the output
of cat $filename is not null, which means that the file does have some text to process. Notice
that we used the double square brackets to allow expansion.
Testing for file attributes
The following are the most commonly used operators to check for different file attributes:
Operator Usage Example
-a The file exists [ -a /etc/passwd ]
-d The file exists and it represents a directory [ -d /var/www ]
-f The file exists and it represents a regular file [ -f /etc/passwd ]
-s The file exists and is not empty [ -s /etc/passwd ]
-w The file is writable (by your user account) [ -w websites.txt ]
-x The file is executable (by your user account) [-x highest.sh ]
-N The file has been modified since it was last read [-N websites.txt ]
-O The file is owned by your account [ -O websites.txt ]
-G The file is owned by your group [ -G websites.txt ]
-nt The first file is newer than the second one [ file1 –nt file2 ]
-ot The first file is older than the second one [ file1 –ot file2 ]
LAB: Applying string comparison to
highest.sh
So far we have used a complex method to test whether or not the filename passed to the script
represents an existing filename: we used the exit status of the ls command. Additionally, we
used the output of the cat command to ensure that it is not empty. But string operators provide
a more elegant way of achieving the same result:
filename=${1:?Please enter a filename}
count=${2:-5}
if [ -s $filename ]; then
sort -n $filename | tail -$count
else
echo "Invalid filename or file is empty"
exit 1
fi
The –s here ensures that the file exists and that it contains text before allowing the sort
command to process it.
Testing for integers
The following are the operators used for integers:
case expression
in
pattern1 )
code ;;
pattern2 )
code ;;
esac
LAB: change highest.sh to allow getting
highest and lowest ranking websites
Using the case statement, we need to add an option that the user can choose whether to get the highest or the lowest ranking websites. Again we
are hardcoding the count variable to be 5 for the sake of simplicity:
filename=${1:?Please enter a filename}
count=5
order=${2:--h}
if [ -s $filename ]; then
case $order
in
*-h )
sort -n $filename | tail -$count ;;
*-l )
sort -rn $filename | tail -$count ;;
* )
echo "Please enter –l or –h"
esac
else
echo "Invalid filename or file is empty"
exit 1
fi
The change made here is introducing the $order variable. It will – optionally – take the second command line argument as either –h (highest
ranking), or –l (lowest ranking), with the default set as –h. The count is set to 5.
Then comes the case statement; the sort command is run normally when $order is –h, and run with the inverse switch (-r), which would make it sort
the list descending.
The use of patterns here is optional. With patterns, the script will interpret any string ending in –h or –l as a valid argument. Notice the use of the
"catch all" statement at the end of case block *) which will get invoked when the user inputs an invalid option.
The select statement
It is only available in BASH and Korn shells.
It allows you to make simple menus from which the user can choose an option, this option will trigger
a configured action.
It has a syntax similar to the for loop:
select variable
[in
set
]
do
code that can reference $variable
done
If you omitted the in set part, it will default to "$@" variable.
When the user selects an option from the menu, that choice is stored in $variable and the number
itself is stored in the built-in variable $REPLY.
LAB: create a simple user menu
To demonstrate select functionality, we are going to create a very simple menu that will allow the user to print his/her login id and to
change the current password:
PS3="Your choice? "
select s in "Print loginid" "Change password" "Exit"; do
case $s in
*loginid )
echo "Your login ID is " $(id -u)
break ;;
*password )
passwd
break ;;
Exit )
break ;;
* )
echo "Invalid selection"
esac
done
We use the keyword break to terminate the menu.
Note that we used the case statement to validate the choice made by the user. The asterisk * is for pattern matching.
If the user pressed RETURN without making a selection, the menu will be re-printed. The only way to exit the menu is to press 3 or type
CTRL-C
The while and until loops
While and until loops are used to execute one or more code statements as long as a condition is
met.
The syntax of while and until is as follows:
while/until condition; do
code
done
The condition is the same one used for the if statement: a command that has the exit status of
0.
The difference between while and until is that the while loop runs as long as the
condition is true, whereas the until loop runs as long as the condition is false.
LAB: write to a file until a certain size
Almost all applications write their events and other data to log files. But those files can quickly get
large so that they fill your space. A good practice is to rotate log files once they reach a certain limit
so that they can be archived. In this lab we'll simulate a log file that is constantly filled with random
data, then use the while loop to monitor its size and rotate it:
timestamp=$(date +%Y%m%d-%H%M%S)
while [[ $(du –k data.log | awk '{print $1}') -gt 100 ]]; do
cp -p data.log data.log.$timestamp
> data.log
done
As soon as the file reaches 100 KB, it will be copied to a new file with a timestamp attached to denote
when the rotation happened, and the file itself is truncated.
The while loop works as long as the condition is true (file size is greater than or equal 100 KB). This
script can be run in the background to ensure proper rotation.
LAB: create a “retry ssh” script
Lots of times you reboot a machine and keep on waiting till it is up so that you can connect to it
using SSH. Every few seconds you try the command until the password prompt is shown. Using
the until loop, we can automate this process as follows: