Friday, February 06, 2004

Unix/Linux Shell Scripts

Shell scripts are very powerful to solve some problems in Unix systems. Among bsh, csh and ksh, Bourne shell is the most commonly used one in the system job handling.

A brief summary of test operators in shell programming:
[ -d name ] true if name is a directory and exists
[ -f name ] true if name is a file and exists
[ -s name ] true if name is an existing, not empty file
[ -r name ] true if name is a readable file (read access)
[ -w name ] true if name is a writeable file (write access)
[ -x name ] true if name is an executable file (execute access)
[ -z name ] true if name has zero length
[ -n name ] true if name has non zero length
[ string ] true if string is not empty
[ name1 = name2 ] true if string name1 and name2 are equal
[ name1 != name2 ] opposite of =
[-d dir1 -a -f prog] -a: "and" (logical "and") -o: " or " (logical "or")
[ ! -f name ] true, if file name does not exist
[ num1 -eq num2 ] true if number num1 and num2 are equal
[ num1 -neq num2 ] true if number num1 and num2 are not equal
[ num1 -lt num2 ] true if number num1 is less than number num2
[ num1 -gt num2 ] true if number num1 is greater than number num2
[ num1 -ge num2 ] true if number num1 is greater than or equal to num2
[ num1 -le num2 ] true if number num1 is less than or equal to num2

Following are some examples:

1. Check your system's info and print the shells available in your system:
#!/bin/sh
# check your system's info and which shells are available
echo "The machine information: `uname -s` version `uname -r`"
for x in sh bsh bash csh tcsh ksh zsh; do
test -x /bin/$x && shells="$shells $x"
done
echo "The following shells are available in the system: $shells"
2. Check whether the input names are executable files from your searching path:
#!/bin/sh
# check whether the input names are executable files from your searching path
# if so, print the whole path of them
mypath=`echo $PATH | tr ':' ' ' `

for filename in $*; do
for apath in $mypath ;do
if [ -x $apath/$filename ]; then
echo $apath/$filename
fi
done
done | while read finded; do
ls -l $finded
done
3. Print shell command arguments in reverse order:
#!/bin/sh
# Print all the arguments reversely

inputString=""
for i in $*
do
inputString="$i $inputString"
done
echo "The reverse arguements you typed are:"
echo $inputString
exit
4. Menu switch:
#!/bin/sh
# interpret the input

echo "Enter a command (list/user/disk/quit/Quit):"
while read cmd
do
case "$cmd" in
list) ls -al ;;
user) who -l ;;
disk) df . ;;
quit|Quit) break ;;
*) echo "$cmd: No such command" ;;
esac
done
echo "All done"
5. Calculate the factorial of a number n!=n*(n-1)!
#!/bin/sh
#calculate the factorial of a number n!=n*(n-1)!

if [ $# -eq 0 ]; then
echo " Usage: factor.sh number"
exit 1
elif [ $# -gt 1 ]; then
echo " Too many arguments"
exit 1
fi

COUNT=0
VALUE=1
while [ $COUNT -lt $1 ]; do
COUNT=`expr $COUNT + 1`
VALUE=`expr $VALUE \* $COUNT`
done

#display the results
echo "The factorial of $1 is $VALUE"
6. Check whether the input words are existing in a given file:
#!/bin/sh
#check if the input are valid words in a specified dictionary
if [ $# -lt 2 ]; then
echo " Usage: check-word.sh dictionary-file word1 word2 ..."
exit 1
fi

COUNT=1
TARGET=$1
shift
for WORD in $*; do
echo -n "$COUNT : "
COUNT=`expr $COUNT + 1`
echo -n "Word $WORD "
CHECK=`eval "grep -w $WORD $TARGET | wc -l"`
if [ $CHECK -eq 0 ]; then
echo "is not found in $TARGET !"
else
echo "is found in $TARGET !"
fi
done
7. Check whether the input names are executable files from your searching path:
#!/bin/sh
# check whether the input names are executable files from your searching path
# if so, print the whole path of them

mypath=`echo $PATH | tr ':' ' ' `

for filename in $*; do
for apath in $mypath ;do
if [ -x $apath/$filename ]; then
echo $apath/$filename
fi
done
done | while read finded; do
ls -l $finded
done
8. Check mail status and display mail headers for new and unread mails:
#!/bin/csh
# check mail status and display mail headers for new and unread mail

set maildir="/var/mail"

if (-e $maildir/$USER.lock == 0) then
if ((-e $maildir/$USER != 0) && (-z $maildir/$USER == 0)) then
mailx -H | awk 'BEGIN { c = 0 } { if (($1 ~ /N/) || ($1 ~ /U/)) {print $0; c = 1} } \
END { if (c == 0) print "no new mail" }'
else
echo ' No mail for '$USER
endif
else
echo ' Your mailbox is locked'
endif
9. Change files' permission in directories and their subdirectories:
#!/bin/sh
# Change the permissions of all files in directories including subtree.
# Use command "find" to print all files(directories) in a path or paths.
# Pipe each file(directory) name to input function "read".
# Then use the script of question1 to change the permission.
# Select one or more directories to change.
# Function current path if without argument.

if [ $1 = ? ] || [ ! -d $1 ]
then echo " Usage: ch-perm.sh [path1] [path2] ... "
exit
fi

# If no argument then set path to current directork
path=${*:-.}

find $path -depth -print | while read file
do
if [ -d $file ]; then
chmod 711 $file
elif [ -x $file ]; then
chmod 755 $file
else chmod 644 $file
fi
done

echo " Job was done successfully!"
exit
10. A recursive version to do the above task:
#!/bin/sh
# set the permissions: directories 711, excutable files 755, else 644.
# [ -n "not_empty_string"]; [ -e "exist_file" ];
# [ -f "regular_file" ]; [ -L "symbol_link" ]

dir=`pwd`
command=$0
echo "Directory: $dir Command: $command"

for file in *
do
if [ -d $file ]
then
echo "directory: $file "
chmod 711 $file
cd $file
$dir/$command
echo "now directory is : `pwd` "
cd ..
elif [ -x $file ]
then
echo "excutable file: $file "
chmod 755 $file
else
echo "regular file: $file "
chmod 644 $file
fi
done

echo " Job was done successfully!"
exit