I often have to code many many scripts for my daily work as a system administrator. In the (vain) hopes these might be useful to someone else, maybe I should release these into the public domain.
My style of coding hasn't really stabilised; still trying to find a style that allows secure coding and easy readability. If you have suggestions, please let me know. 
Of course, if there a bugs, please let me know. Thanks! 
I went looking around for bash scripting tips, especially secure coding of bash. Can't find much information, so decided to consolidate whatever I found here. 
if [[ "a$?" == "a4" ]]; then
/bin/grep "hardcoding the full path" *
echo=/bin/echo
${echo} "From bash manpage under EXPANSION:
The order of expansions is: brace expansion, tilde expansion, parameter,
variable and arithmetic expansion and command substitution (done in a
left-to-right fashion), word splitting, and pathname expansion."
#!/bin/bash
# comments
PATH=/bin:/usr/bin
function print_usage () {
${echo} "
$0
Usage: $0 [-a opts] [arguments]
or $0 -h
Description: Something fishy
Options:
-a opts (Optional) Options
-h (Optional) Help
arguments Smelly smelly fish
"
}
if [ $# -lt 2 ]; then
print_usage
exit 1
else
while getopts ha:b: options; do
case "${options}" in
h) print_usage
exit 1
;;
a) flag=${options}
;;
b) flag=${options}
;;
*) echo "default case, everything else fits here"
;;
esac
done
shift $((${OPTIND} - 1))
a=erie
ab=were
if [[ "${a}b" == "erieb" ]]; then
Basket 1.0 is out. And it looks better than ever. Instead of just to-do lists, clipboard stacks, and checklists, there's now a new freestyle basket!
Some other new features include import (text, HTML, "basket", Knotes, Sticky Notes, etc) and export (HTML or "basket" formats), password protection, integration with Kontact, and a lot more. It's looking more full featured now. Yeah... 
[Screenshot hosted on Flickr]
Wow! Found this gem when researching on BIND hardening: Rob Thomas' CYMRU Secure BIND Template.
O'Reilly also published an excerpt of their DNS book (DNS and BIND, by Paul Albitz and Cricket Liu), which detailed more security tips. Yes, this book can be found in the National Library too.
During a security scan, one of my DNS servers was fingerprinted by NESSUS, so I think I will disable all CHAOS queries from now on.... Grrr......
Okay, okay, sorry for the sensationalist headline.
But I really bought a Microsoft Comfort Curve Keyboard 2000. My wrist is starting to hurt, so decided to prevent carpal tunnel syndrome before it is too late.
Well, from my head-spinning survey of the keyboards in Sim LIm Square, there are only 4 types of ergonomic keyboards available.
A4 Tech Anti-RSI A-Shaped keyboards
Microsoft Comfort Curve keyboard
Microsoft Natural Ergonomic KeyboardOf course, the first one is out.
I tried one of the A-Shaped keyboards, and it felt weird. Now, I can touch-type, so the key positioning does not feel out of place. However, the A-Shape doesn't feel ergonomic. My wrists were still skewed, though not as much as before.
That left the Microsoft keyboards. The Natural Ergonomic keyboard costs a hefty $80 dollars!! Typical Microsoft over-pricing, I guess. So that leaves the more affordable Comfort Curve keyboard selling at $28.
The Comfort Curve keyboard basically elongates the middle keys and spreads the keyboard over a curve. This allows a touch-typist (both systematic and non-systematic typists) to keep his/her wrists straight, while allowing two-finger peckers to peck away as usual. Isn't that great? Compare this with the A-Shape keyboard or the Natural Ergonomic keyboard.
A soft tactile keyboard, the keys feel responsive, and they are relatively quiet. Just the kind of keyboard I like.
Think I will get another, one for use at home, the other in the linuxNUS clubroom. 
The packaging is not environmentally friendly though. The keyboard is packed in 1) a plastic bag, 2) a carton box, 3) a box sleeve. Is there a need to use so much material just for the packaging?!?
Oh yes, not to mention the two product manuals, which states (among other irrelevant things like optical laser precautions (!?!), battery powered precautions (?!?), etc) the keyboard can lead to "serious injuries or disorders". IT also warns one not to "ignore these warning signs", "promptly see a qualified health professional" for these "musculoskeletal disorders". All in good sense, but isn't that why one is buying this keyboard in the first place?!?!
Okay, the other product manual is more applicable, showing how one plugs in the keyboard to a USB port.
Typical of a Microsoft product, the packaging states that:
I guess this means people who are using Windows 9x, Linux, Unix, BSD, Mac Tiger (10.4) should change their operating systems. Or the use of the keyboard on these systems invalidates the warranty? Sheesh...
Altogether, the keyboard is great, but the marketing leaves much to be desired. Sigh.
As an open source advocate, it's very tempting to paint all and any Microsoft product as "evil", "monopolistic" or "over-priced". However, doing so puts us at the same level as them. Being responsible (and successful) advocates, we have to be very clear on the good and the bad of both camps, and recommend F/OSS when it makes sense to do so. Pushing F/OSS and Linux when it is clearly not suitable just sets up the case for failure, which would not help our cause.
At least, in this case, the Microsoft keyboards are clearly better choices, so why insist on the other products? That's what market competition is all about, isn't it?
Cool... Kev just introduced me to Kooldock, a fast clone of the Mac OSX dock.
Adding of applications is by either adding the application, or copying the application's .desktop file to the kooldock preferences directory. Automatically hidden till the mouse is at the bottom edge too! Very intuitive, responsive and well, kewl... 
Now that I am doing cross platform system administration, it is getting critical to have lists of equivalent commands across the *nixes. Found 2 guides so far:
Will be adding more as the time goes by. 
As mentioned previously, I had to mirror a server for testing purposes across two networks through a computer in the middle. The best way I found was to do an rsync over ssh, but this requires a non-password authentication, hence I have to set up a RSA key-pair login.
First, the ssh server must allow this authentication method. Make sure /etc/ssh/sshd_config has the following:
Protocol 2 # use ssh protocol 2!!
# Authentication
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
Restart your ssh server. You might want to start another ssh session first, in case there's something wrong and your ssh server can't restart.
Make sure you are the only user able to access ~/.ssh/authorized_keys by changing the permissions to 600.
[user@server] $ chmod 600 ~/.ssh/authorized_keys
The default location of the RSA key-pair is in ~/.ssh/id_rsa (private key) and ~/.ssh/id_rsa.pub (public key). This is set in /etc/ssh/ssh_config of your client computer, under IdentityFile.
In your own account, generate a RSA key-pair. You do not need a password passphrase. Store the keys in the location indicated in your ssh client configuration.
[user@client] $ ssh-keygen -t rsa
Change the permissions such that only you can read the files.
[user@client] $ chmod 600 ~/.ssh/id_rsa
[user@client] $ chmod 600 ~/.ssh/id_rsa.pub
Append your public key to the ssh server. Of course you can cut and paste, or you can do this:
[user@client] $ cat ~/.ssh/id_rsa.pub | ssh username@ssh.server "cat - >> ~/.ssh/authorized_keys"
If everything works, you should be able to login without typing in any passwords. Hooray!
Here's another script I did when I had to reorganised a folder hierarchy of years of data. Basically to ensure files are not missing, or corrupted.
#!/bin/bash
#########################
#
# checkNoMissingFiles
# ===================
#
# This script checks that no files are missing after folders are reorganised.
# Basic algorithm is to checksum all files in both old and new folders, then
# checking through both lists of checksums to ensure all checksums are present
# in both lists.
#
# Changelog
# =========
#
# 18 Oct 2007 - Junhao
# * Initial commit
#
# 11 Dec 2007 - Junhao
# * Tidied style
# * Fixed bug with spaces in filenames
# * added option to save generated checksums
# * changed md5sum to sha1sum
# * changed checksum to general algorithm
#########################
PATH=/bin:/usr/bin
export PATH
## Program Locations
awk=/bin/awk
cat=/bin/cat
echo=/bin/echo
find=/usr/bin/find
grep=/bin/grep
checksum=/usr/bin/sha1sum
mktemp=/bin/mktemp
rm=/bin/rm
tee="/usr/bin/tee -a"
touch=/bin/touch
## End Program Locations
## Start Script
## Script parameters
f_logFile=/dev/null
d_orgLoc=/dev/null
d_newLoc=/dev/null
v_oldFileName=
v_oldFileChksum=
f_oldChksumLog=
f_newChksumLog=
v_missingFilesCount=0
v_missingFiles=""
v_output=
v_f1flag=1
v_f2flag=1
## End Script parameters
function print_usage () {
${echo} "
$0
Usage: $0 [-L logfile] [-f1 filename] [-f2 filename] [oldDir] [newDir]
or $0 -h
Description: Checks that there are no missing files after reorganising a directory.
Options:
-L logfile (Optional) Path to log file
-h (Optional) This help text
-1 (Optional) Filename to save checksum for old directory
-2 (Optional) Filename to save checksum for new directory
oldDir Location of old directory
newDir Location of new directory
"
}
if [ $# -lt 2 ]; then
print_usage
exit 1
else
while getopts hL:1:2: options; do
case "${options}" in
h) print_usage
exit 1
;;
L) f_logFile=${OPTARG}
;;
1) f_oldChksumLog=${OPTARG}
v_f1flag=0
;;
2) f_newChksumLog=${OPTARG}
v_f2flag=0
;;
*) f_logFile=/dev/null
;;
esac
done
shift $((${OPTIND} - 1))
if [ -d "$1" ]; then
d_orgLoc="$1"
else
${echo} "Error: Original directory does not exist!"
print_usage
exit 1
fi
if [ -d "$2" ]; then
d_newLoc="$2"
else
${echo} "Error: New directory does not exist!"
print_usage
exit 1
fi
if [ -z ${f_oldChksumLog} ]; then
f_oldChksumLog=`${mktemp}`
elif [ -f ${f_oldChksumLog} ]; then
${echo} "Error: File ${f_oldChksumLog} exists! Please give another filename."
exit 2
else
${touch} ${f_oldChksumLog}
if [ ! -f ${f_oldChksumLog} ]; then
${echo} "Error: ${f_oldChksumLog} cannot be created!"
exit 4
fi
fi
if [ -z ${f_newChksumLog} ]; then
f_newChksumLog=`${mktemp}`
elif [ -f ${f_newChksumLog} ]; then
${echo} "Error: File ${f_newChksumLog} exists! Please give another filename."
exit 3
else
${touch} ${f_newChksumLog}
if [ ! -f ${f_newChksumLog} ]; then
${echo} "Error: File ${f_newChksumLog} cannot be created!"
exit 5
fi
fi
fi
${echo} "${find} \"${d_orgLoc}\" -type f -exec ${checksum} \"\\{\\}\" \\;" | ${tee} ${f_logFile}
${find} "${d_orgLoc}" -type f -exec ${checksum} "{}" \; | ${tee} ${f_oldChksumLog}
${echo} "${find} \"${d_newLoc}\" -type f -exec ${checksum} \"\\{\\}\" \\;" | ${tee} ${f_logFile}
${find} "${d_newLoc}" -type f -exec ${checksum} "{}" \; | ${tee} ${f_newChksumLog}
while read -r v_oldFileChksum v_oldFileName; do
if [[ `${grep} ${v_oldFileChksum} ${f_newChksumLog}` ]]; then
v_output="Okay: ${v_oldFileName} -> "
v_output="${v_output} `${grep} \"${v_oldFileChksum}\" \"${f_newChksumLog}\" | ${awk} '{print $2}'`"
else
v_output="ERROR: ${v_oldFileName} is missing"
v_missingFiles="${v_missingFiles} ${v_oldFileName}"
v_missingFilesCount=$((v_missingFilesCount+1))
fi
${echo} "${v_output}" | ${tee} ${f_logFile}
done < ${f_oldChksumLog}
#### cleanup ####
if [ "1" == ${v_f1flag} ]; then
${rm} ${f_oldChksumLog}
fi
if [ "1" == ${v_f2flag} ]; then
${rm} ${f_newChksumLog}
fi
if [ ${v_missingFilesCount} -gt 0 ]; then
${echo} "ERROR: ${v_missingFilesCount} files are missing:" | ${tee} ${f_logFile}
${echo} "ERROR: ${v_missingFiles}" | ${tee} ${f_logFile}
exit 99
else
${echo} "Success: ${v_missingFilesCount} files are missing" | ${tee} ${f_logFile}
exit 0
fi
I have been using this script to check for updates on my Redhat systems for quite some time. Put this into your cron.daily, and you have a daily nag to update your system. 
#!/bin/bash
#########
## Yum Check Update Script
##
## This script checks for system updates and sends email
## to sysmin team if there are any updates.
##
## Changelog
## ---------
## 24 Oct 2008 (Junhao)
## - Initial commit
##
#########
_CAT="/bin/cat"
_DATE="/bin/date"
_HOSTNAME="/bin/hostname"
_MAILX="/bin/mailx"
_RM="/bin/rm"
_TOUCH="/bin/touch"
_YUM="/usr/bin/yum"
HOSTNAME=`${_HOSTNAME}`
DATESTAMP=`${_DATE} +%Y%b%d-%H:%M:%S`
EMAIL=root
MAILSUB="RHEL Update Available for ${HOSTNAME} on ${DATESTAMP}"
TEMPLOG=/tmp/yum-check-update.tmp
${_TOUCH} ${TEMPLOG}
${_YUM} check-update 1> ${TEMPLOG} 2>&1
if [[ $? != 0 ]]; then
${_CAT} ${TEMPLOG} | ${_MAILX} -s "${MAILSUB}" ${EMAIL}
fi
${_RM} ${TEMPLOG}
I am currently migrating out of Zimbra to a 3rd-party host. Just for archival, here's my Zimbra backup script. Just run this script using a cronjob every day. There will be a short downtime where Zimbra is shutdown to synchronise the last bit of emails, but that should be okay if you have a backup MX server.
This script creates a working live copy of the Zimbra directory, then shutdown Zimbra to sync the directory. The directory is then passed through star, into a small(er) file.
#!/bin/bash
########
## Zimbra backup script
##
## See
##
## Requires star, rsync, bash, gzip
## Does full backups of /opt/zimbra only. Tries to
## minimise zimbra shutdown time with a live rsync,
## then a offline rsync
##
## Changelog
## ---------
## 15 Feb 2009 (Junhao)
## - BUGFIX: deletes leftover archive.tgz tarball before
## creating new tar
## 21 Oct 2008 (Junhao)
## - added disk size to log
## - added tarball -t test
## 19 Oct 2008 (Junhao)
## - Initial commit
##
########
## config
_AWK=`which awk`
_CAT=`which cat`
_CD=cd #bash builtin
_CHECKSUM=`which sha1sum`
_DATE=`which date`
_DF=`which df`
_DU=`which du`
_ECHO=`which echo`
_MV=`which mv`
_MAIL=`which mail`
_RSYNC=`which rsync`
_RM=`which rm`
_SLEEP=`which sleep`
_SU=`which su`
_TAR=`which star`
_TOUCH=`which touch`
DATESTAMP=`date +%Y%b%d-%T`
RELEASE=`${_SU} - zimbra -c"zmcontrol -v"`
RELEASE=`${_ECHO} ${RELEASE} | ${_AWK} '{ print $2"-"$3"-"$4 }'`
TARBALL=zimbra-${RELEASE}-backup-full-${DATESTAMP}.tgz
LOGFILE=zimbra-${RELEASE}-backup-full-${DATESTAMP}.log
EMAIL=user@domain.co.m
MAILSUB="ZCS Backup Report on ${DATESTAMP}"
BKUPRETRIES=5
RESTARTRETRIES=100
ZIMBRADIR=/opt/zimbra
BASEDIR=/opt/zimbra-backups
WORKDIR=${BASEDIR}/working
LOGDIR=${BASEDIR}/logs
SAVEDIR=${BASEDIR}/saved
LOG=${LOGDIR}/${LOGFILE}
CHKSUMLOG=${SAVEDIR}/checksum
TARLOG=${WORKDIR}/tar.log
LOCK=${BASEDIR}/zimbra-backup.lock
TEMPARCHIVE=archive.tgz
function calc_downtime() {
if [ -z "${STARTTIME3}" ]; then
STARTTIME3=`date +%s`
fi
if [ -z "${ENDTIME}" ]; then
ENDTIME=`date +%s`
fi
TOTAL=$((${ENDTIME} - ${STARTTIME1}))
OFFLINE=$((${STARTTIME3} - ${STARTTIME2}))
${_ECHO} "Time taken: $((${TOTAL} / 3600)) hours $((${TOTAL} % 3600 / 60)) minutes $((${TOTAL} % 3600 % 60)) seconds" >> ${LOG}
${_ECHO} "Zimbra Offline: $((${OFFLINE} / 3600)) hours $((${OFFLINE} % 3600 / 60)) minutes $((${OFFLINE} % 3600 % 60)) seconds" >> ${LOG}
return 0
}
function force_restart() {
for (( i=0; i<=${RESTARTRETRIES} ; i=$(($i+1)) )); do
${_SU} - zimbra -c"zmcontrol stop"
${_SLEEP} 10
${_SU} - zimbra -c"zmcontrol start"
${_SLEEP} 10
${_SU} - zimbra -c"zmcontrol status"
if [[ $? == 0 ]]; then
${_ECHO} "Sucessfully restarted zimbra after $((${i}+1)) tries" >> ${LOG}
return 0
else
${_SLEEP} 10
fi
done
${_ECHO} "Could not restart zimbra after ${RESTARTRETRIES} tries" >> ${LOG}
return 254
}
function lock_set() {
${_TOUCH} ${LOCK}
}
function lock_remove() {
${_RM} ${LOCK}
}
function synchronise() {
for (( i=0; i<=${BKUPRETRIES} ; i=$(($i+1)) )); do
${_RSYNC} -avHK --delete --exclude=*.pid ${ZIMBRADIR} ${WORKDIR}
if [[ $? == 0 || $? == 24 ]]; then
return $?
fi
${_SLEEP} 10
done
return 254
}
function send_mail() {
calc_downtime
${_ECHO} "" >> ${LOG}
${_SU} - zimbra -c"zmcontrol status" >> ${LOG}
${_ECHO} "" >> ${LOG}
${_CAT} ${TARLOG} >> ${LOG}
${_RM} ${TARLOG}
${_CAT} ${LOG} | ${_MAIL} -s "${MAILSUB}" ${EMAIL}
}
function send_error() {
MAILSUB="[Failed] ${MAILSUB}"
send_mail
}
function send_success() {
MAILSUB="[Success] ${MAILSUB}"
send_mail
}
${_TOUCH} ${LOG}
lock_set
${_ECHO} "Server: `${_SU} - zimbra -c"zmhostname"`" >>${LOG}
${_ECHO} "Tarball: ${TARBALL}" >> ${LOG}
${_ECHO} "Logfile: ${LOG}" >> ${LOG}
${_ECHO} "Backup started at ${DATESTAMP}" >> ${LOG}
${_ECHO} "" >> ${LOG}
## Outputs time backup started for logging
STARTTIME1=`${_DATE} +%s`
## Online sync to working directory
${_ECHO} "Starting online rsync" >> ${LOG}
synchronise
if [[ $? == 254 ]]; then
${_ECHO} "Error in creating live copy" >> ${LOG}
send_error
lock_remove
exit 1
fi
STARTTIME2=`${_DATE} +%s`
## Shut down zimbra
${_SU} - zimbra -c"zmcontrol stop"
if [[ $? != 0 ]]; then
${_ECHO} "Error stopping zimbra" >> ${LOG}
${_SU} - zimbra -c"zmcontrol status" >>${LOG}
${_ECHO} "Aborting backup, force restarting zimbra" >> ${LOG}
force_restart
if [[ $? == 255 ]]; then
${_ECHO} "Trying again" >> ${LOG}
force_restart
fi
send_error
lock_remove
exit 2
fi
${_SLEEP} 10
## Offline sync to working directory
${_ECHO} "Starting offline rsync" >>${LOG}
synchronise
if [[ $? == 0 ]]; then
${_ECHO} "Offline rsync completed successfully" >> ${LOG}
elif [[ $? == 24 ]]; then
## some files disappeared, meaning some open process running
## wait a while, rerun rsync, and assume okay
${_SLEEP} 60
synchronise
else
${_ECHO} "Error in creating offline copy" >> ${LOG}
${_ECHO} "Aborting backup, force restarting zimbra" >> ${LOG}
force_restart
send_error
lock_remove
exit 3
fi
## Start zimbra
force_restart
if [[ $? == 254 ]]; then
${_ECHO} "Trying again" >> ${LOG}
force_restart
send_error
lock_remove
exit 4
fi
STARTTIME3=`date +%s`
## Synchronization sucessful, create archive
${_CD} ${WORKDIR}
if [ -f ${TARLOG} ]; then
${_RM} ${TARLOG}
else
${_TOUCH} ${TARLOG}
fi
if [ -f ${TEMPARCHIVE} ]; then
${_RM} ${TEMPARCHIVE}
fi
${_TAR} czf ${TEMPARCHIVE} ./ 1>> ${TARLOG} 2>&1
if [[ $? != 0 && $? != 254 ]]; then
${_ECHO} "Error creating tarball. Error Code: $?" >> ${LOG}
${_ECHO} "Aborting backup" >> ${LOG}
${_ECHO} "Tarball location: ${WORKDIR}/${TEMPARCHIVE}" >> ${LOG}
send_error
lock_remove
exit 5
fi
${_TAR} ztf ${TEMPARCHIVE}
if [[ $? == 0 ]]; then
${_MV} ${TEMPARCHIVE} ${SAVEDIR}/${TARBALL}
${_ECHO} "" >> ${LOG}
${_DU} -sh ${SAVEDIR}/${TARBALL} >> ${LOG}
${_DF} -h ${SAVEDIR}/${TARBALL} >> ${LOG}
else
${_ECHO} "Error validating tarball. Error Code: $?" >> ${LOG}
${_ECHO} "Aborting backup" >> ${LOG}
${_ECHO} "Tarball location: ${WORKDIR}/${TEMPARCHIVE}" >> ${LOG}
send_error
lock_remove
exit 6
fi
## Creating checksum
${_CD} ${SAVEDIR}
CHKSUM=`${_CHECKSUM} ${TARBALL}`
${_ECHO} "${CHKSUM}" >> ${CHKSUMLOG}
${_ECHO} "" >> ${LOG}
${_ECHO} "Checksum using ${_CHECKSUM}">> ${LOG}
${_ECHO} "${CHKSUM}" >> ${LOG}
## Backup done!
ENDTIME=`date +%s`
send_success
lock_remove
exit 0
*Updated: 11 Dec 2007
I'm wondering where I should store the scripts I'm writing. Out of pure laziness, I'll just dump them as my blog entry for now. 
Here's a script to check for missing files after a directory has been re-organised. Basically, it compares the md5sum of the files in the old directory and the new directory.
Please let me know if there are any bugs. 
#!/bin/bash
#########################
#
# checkNoMissingFiles
# ===================
#
# This script checks that no files are missing after folders are reorganised.
# Basic algorithm is to checksum all files in both old and new folders, then
# checking through both lists of checksums to ensure all checksums are present
# in both lists.
#
# Changelog
# =========
#
# 18 Oct 2007 - Junhao
# * Initial commit
#
# 11 Dec 2007 - Junhao
# * Tidied style
# * Fixed bug with spaces in filenames
# * added option to save generated checksums
# * changed md5sum to sha1sum
# * changed checksum to general algorithm
#########################
PATH=/bin:/usr/bin;
## Program Locations
awk=/usr/bin/awk
cat=/usr/bin/cat
echo=/usr/bin/echo
find=/usr/bin/find
grep=/bin/grep
checksum="/usr/bin/sha1sum"
mktemp=/bin/mktemp
rm=/usr/bin/rm
tee="/usr/bin/tee -a"
touch="/bin/touch"
## End Program Locations
## Start Script
## Script parameters
f_logFile=/dev/null
d_orgLoc=/dev/null
d_newLoc=/dev/null
v_oldFileName=
v_oldFileChksum=
f_oldChksumLog=
f_newChksumLog=
v_missingFilesCount=0
v_missingFiles=""
v_output=
v_f1flag=1
v_f2flag=1
## End Script parameters
function print_usage () {
${echo} "
$0
Usage: $0 [-L logfile] [-f1 filename] [-f2 filename] [oldDir] [newDir]
or $0 -h
Description: Checks that there are no missing files after reorganising a directory.
Options:
-L logfile (Optional) Path to log file
-h (Optional) This help text
-1 (Optional) Filename to save checksum for old directory
-2 (OPtional) Filename to save checksum for new directory
oldDir Location of old directory
newDir Location of new directory
"
}
if [ $# -lt 2 ]; then
print_usage
exit 1
else
while getopts hL:1:2: options; do
case "${options}" in
h) print_usage
exit 1
;;
L) f_logFile=${OPTARG}
;;
1) f_oldChksumLog=${OPTARG}
v_f1flag=0
;;
2) f_newChksumLog=${OPTARG}
v_f2flag=0
;;
*) f_logFile=/dev/null
;;
esac
done
shift $((${OPTIND} - 1))
if [ -d $1 ]; then
d_orgLoc=$1
else
${echo} "Error: Original directory does not exist!"
print_usage
exit 1
fi
if [ -d $2 ]; then
d_newLoc=$2
else
${echo} "Error: New directory does not exist!"
print_usage
exit 1
fi
if [ -z ${f_oldChksumLog} ]; then
f_oldChksumLog=${mktemp}
elif [ -f ${f_oldChksumLog} ]; then
${echo} "Error: File ${f_oldChksumLog} exists! Please give another filename."
exit 2
else
${touch} ${f_oldChksumLog}
if [ ! -f ${f_oldChksumLog} ]; then
${echo} "Error: ${f_oldChksumLog} cannot be created!"
exit 4
fi
fi
if [ -z ${f_newChksumLog} ]; then
f_oldChksumLog=${mktemp}
elif [ -f ${f_newChksumLog} ]; then
${echo} "Error: File ${f_newChksumLog} exists! Please give another filename."
exit 3
else
${touch} ${f_newChksumLog}
if [ ! -f ${f_newChksumLog} ]; then
${echo} "Error: File ${f_newChksumLog} cannot be created!"
exit 5
fi
fi
fi
${echo} "${find} \"${d_orgLoc}\" -type f -exec ${checksum} \\"\{\}\\" \;" | ${tee} ${f_logFile}
${find} "${d_orgLoc}" -type f -exec ${checksum} \"\{\}\" \; | ${tee} ${f_oldChksumLog}
${find} "${find} \"${d_newLoc}\" -type f -exec ${checksum} \\"\{\}\\" \;" | ${tee} ${f_logFile}
${find} "${d_newLoc}" -type f -exec ${checksum} \"\{\}\" \; | ${tee} ${f_newChksumLog}
while read -r v_oldFileChksum v_oldFileName; do
if [[ `${grep} ${v_oldFileChksum} ${f_newChksumLog}` ]]; then
v_output="Okay: ${v_oldFileName} -> "
v_output="${v_output} `${grep} \"${v_oldFileChksum}\" \"${f_newChksumLog}\" | ${awk} '{print $2}'`"
else
v_output="ERROR: ${v_oldFileName} is missing"
v_missingFiles="${v_missingFiles} ${v_oldFileName}"
v_missingFilesCount=$((v_missingFilesCount+1))
fi
${echo} "${v_output}" | ${tee} ${f_logFile}
done < ${f_oldChksumLog}
#### cleanup ####
if [ "1" == ${v_f1flag} ]; then
${rm} ${f_oldChksumLot}
fi
if [ "1" == ${v_f2flag} ]; then
${rm} ${f_newChksumLog}
fi
if [ ${v_missingFilesCount} -gt 0 ]; then
${echo} "ERROR: ${v_missingFilesCount} files are missing:" | ${tee} ${f_logFile}
${echo} "ERROR: ${v_missingFiles}" | ${tee} ${f_logFile}
exit 99
else
${echo} "Success: ${v_missingFilesCount} files are missing" | ${tee} ${f_logFile}
exit 0
fi
Encountered a problem deleting a file with the name "--option=backup" (without the quotes). Don't ask me how it got there, I have no idea. It can't be deleted using rm or renamed using mv.
[user@system /]# rm '--owner=backup'
rm: unrecognized option `--owner=backup'
Try `rm --help' for more information.
[user@system /]# mv '--owner=backup' nothing
mv: unrecognized option `--owner=backup'
Try `mv --help' for more information.
Solution? Shyam Mani suggested unlink '--option=backup'. Apparently, it directly uses the Linux unlink system call to delete files, so does not encounter the same problem as rm
Update 1: Some people suggested alternatives like rm -- '--option=backup', or rm ./--option=backup. Both worked nicely.
Update 2: And yes, Konqueror can delete that file too. Why didn't I think of that? 
After struggling to get my PCMCIA airo350 wireless card to work with PEAP in NUS for years, I finally caved in and used ndiswrapper. I know, I know, kernel 2.6.17 and above is supposed to support it, but that is for cards with firmware version 5.30.17 "or later". My card is 5.60.x, which is too much later, I guess.
So, no choice but to use ndiswrapper. So I boot to Windows, and checked out the files used by the Aironet 350 device (driver properties -> files), and grabbed the files c:\windows\inf\netx500.inf and c:\windows\system32\drivers\pcx500.sys. Just FYI, pcx500mp.sys is for the Aironet 350 Mini-PCI card.
Then I boot into Linux, and installed ndiswrapper(1.33), and wpa_supplicant (v0.5.7).
Next, I have to install the drivers. This was easy. First make sure the 2 files are in the same directory, then install the driver.
# install the driver
root $ /sbin/ndiswrapper -i /path/to/netx500.inf
# check the driver is installed
root $ /sbin/ndiswrapper -l
netx500 : driver installed
# insert the pcmcia card, then load ndiswrapper
root $ modprobe ndiswrapper
# check ndiswrapper is loaded
root $ lsmod
Module Size Used by
ndiswrapper 195284 0
root $ dmesg
[...]
ndiswrapper version 1.33 loaded
usbcore: registered new interface driver ndiswrapper
There's a problem. Ndiswrapper is not able to automatically create the PCMCIA configuration. I have to do this by manually. I wrote one using the configuration it has already generated (14B9:0350.5.conf) as a guide.
# save this as /path/to/ndiswrapper/netx500/14B9:929B.8.conf (e.g /etc/ndiswrapper/netx500/14B9\:929B.8.conf)
# the '8' in the filename is because CardBus is device type 8.
NdisVersion|0x50001
Environment|1
class_guid|4d36e972-e325-11ce-bfc1-08002be10318
NetworkAddress|XX:XX:XX:XX:XX:XX
driver_version|,07/01/2001,7.29.0.0
BusType|8
FormFactor|PCMCIA
InfrastructureMode|1
LowerRange|ethernet
MediaDisconnectDamper|10
NodeName|
PowerSaveMode|0
RadioName|PC3500
Service|PCX500
SSID1|
SupportedRates|0
UpperRange|ndis5
Now we have to link the Cardbus PCMCIA slot to this configuration. As Cardbus slots are detected as PCI devices, we can use lspci.
# this is the CardBus device we have to take note of
root $ lspci
[...]
01:0b.0 CardBus bridge: Toshiba America Info Systems ToPIC95 (rev 07)
# Get the manufacturer's device ID (xxxx:xxxx)
root $ lspci -n
[...]
01:0b.0 0607: 1179:060a (rev 07)
# create a symlink for the configuration to this device
# Note the capital letters
root $ cd /directory/of/14B9:929B.8.conf
root $ ln -s 14B9:929B.8.conf 1179\:060A.5.conf
root $ ls -al
1179:060A.5.conf -> 14B9:929B.8.conf
14B9:0340.5.conf
14B9:0350.5.conf
14B9:4800.5.conf
14B9:929B.8.conf
netx500.inf
pcx500.sys
The kernel might already have the opensource driver running. To prevent it from loading these modules (aka drivers) instead, we need to black list them in /etc/modprobe.conf
# blacklist the modules
blacklist airo_cs
blacklist airo
# alias the interface to eth1 (or wlan0 if that's your
alias eth1 ndiswrapper
So now we have installed and setup ndiswrapper for this PCMCIA card.
Restart the system!!! Okay, there's no need to do that, but I'm lazy...
Besides, I just upgraded a ton of other stuff too...
root $ modprobe ndiswrapper
root $ lsmod
Since I'm using PEAP (yes, I know it's not secure. NUS doesn't care.... It's their newly installed "secure" wireless system.), I have to get wpa_supplicant to work too.
Hmm... I'm getting tired of typing. I'll just plonk my wpa_supplicant configuration here then... 
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
ap_scan=2 # seem to work only if set to 2
eapol_version=2
network={
disabled=0
priority=1
ssid="NUS"
scan_ssid=1
key_mgmt=IEEE8021X
eap=PEAP
phase2="auth=MSCHAPV2"
identity="user@nus.edu.sg"
password="password"
ca_cert="/path/to/ase1.pem"
}
network={
disabled=1
ssid="NUSOPEN"
scan_ssid=1
key_mgmt=NONE
priority=2
}
network={
disabled=1
key_mgmt=NONE
priority=-9999999
}
Okok... I won't leave you in the berth. Here's a howto on NUS PEAP configuration