RSS Feed Share on Twitter

All Shell Scripting Tips

22 Nov 2018

cd $LOG_DIR

An unexpected side-effect

It is not uncommon to find shell scripts which test that a command worked by checking the $? return-code variable after calling the command, yet take simple things like "cd" for granted:

#!/bin/bash
# Backup script.
# Usage: backup APP_DIR LOG_DIR

APP_DIR=$1
LOG_DIR=$2
cd "$APP_DIR"/bin
./run_backups
if [ "$?" -eq "0" ]; then
  echo "Backups succeeded... deleting older logfiles"
  cd "$LOG_DIR"
  find . -mtime +7 -exec rm {} \;
  exit 0
else
  echo "FATAL: Backups failed."
  exit 1
fi

If you call this script as "backup /usr/local/myapp /var/local/myapp/log" it will go to the "/usr/local/myapp/bin" directory, run the "./run_backups" command, and check the return code. If all went well, it will purge some of the older log files from "/var/local/myapp/logs". Otherwise, it reports a failure message and exits with a non-zero return code.

So far, so good. Could be better, but it does some error checking, lets the caller know if it succeeded or not, and even tidies up some old log files along the way.

There are some things it doesn't check, however:

  • It doesn't check that $LOG_DIR is a directory. What if it was a plain file?
  • It doesn't check that the "cd $LOG_DIR" command succeeded. What if you don't have permission to change to it?
  • Do you need to check that the directory hasn't been moved somewhere else via a symbolic link? The readlink utility can help to check for such things, but it is a little bit tricky to use.
  • It doesn't even check that $LOG_DIR has been provided. What if you called the script with $APP_DIR but no $LOG_DIR?

The final one is possibly the most serious, due to a "feature" of the way in which "cd" works: If you call "cd /var/local/myapp/logs" it will (try to) change to that directory. However, if you call "cd" by itself, it will take you to your home directory. Do you want it to change to your home directory and then blindly start deleting any files over 7 days old?

CD Error

There is one simple method which deals with all of these questions: Simply check your current location after the cd command:

#!/bin/bash
# Backup script.
# Usage: backup APP_DIR LOG_DIR

APP_DIR=$1
LOG_DIR=$2
cd "$APP_DIR"/bin
./run_backups
if [ "$?" -eq "0" ]; then
  echo "Backups succeeded... deleting older logfiles"
  cd "$LOG_DIR"
  pwd | grep "^${LOG_DIR}$"
  if [ "$?" -ne "0" ]; then
    echo "FATAL: Failed to change to \"$LOG_DIR\". Will not attempt to remove old logs."
    exit 2
  fi
  find . -mtime +7 -exec rm {} \;
  exit $?
else
  echo "FATAL: Backups failed."
  exit 1
fi

This simple change check that the pwd command shows exactly "^${LOG_DIR}$" - the "^" at the start ensures that the line starts here, and the "$" at the end ensures that it ends there. So if you ended up in /home/badguy/var/local/myapp/logs or in /var/local/myapp/logs/badguy it would fail.

Equally, if "$LOG_DIR" was empty, you would end up in your home directory, which would not match the empty string. Even if you're root, $HOME must contain at the very least "/".

So this is the shortest, simplest, and most effective way that I know of to check that the $LOG_DIR is valid. For better error reporting, checking the other conditions would also be a good idea, but as a quick-and-easy way of assuring condidence that you are where you need to be, this one simple check pretty much covers all the eventualities listed above.

 

 

 


You can buy the content of this tutorial as a PDF to download to all of your devices!

Contact

You can mail me with this form. If you expect a reply, please ensure that the address you specify is valid. Don't forget to include the simple addition question at the end of the form, to prove that you are a real person!