Self-updating Bash Script Revised

I recently posted an approach for a self-updating bash script. Shortly after using it in production, I noticed a critical shortcoming.

Due to the simple way in which I implemented my update checking (an MD5 sum over the script itself), changes to the script interpreter line (aka shebang) would cause the script to output that a new version was available.

In addition to that, if the user did perform the update, his modification to the interpreter line would be overwritten.

So here is the revised version, that respects the first line on the script itself:

#!/bin/bash

set -o nounset
set -o errexit

SELF=$(basename $0)

# The base location from where to retrieve new versions of this script
UPDATE_BASE=http://typo3scripts.googlecode.com/svn/trunk

# Update check
function updateCheck() {
  SUM_LATEST=$(curl $UPDATE_BASE/versions 2>&1 | grep $SELF | awk '{print $2}')
  SUM_SELF=$(tail --lines=+2 "$0" | md5sum | awk '{print $1}')
  if [[ "" == $SUM_LATEST ]]; then
    echo "No update information is available for '$SELF'" >&2
    echo "Please check the project home page http://code.google.com/p/typo3scripts/." >&2
    
  elif [[ "$SUM_LATEST" != "$SUM_SELF" ]]; then
    echo "NOTE: New version available!" >&2
  fi
}

# Self-update
function runSelfUpdate() {
  echo "Performing self-update..."
  
  _tempFileName="$0.tmp"
  _payloadName="$0.payload"
  
  # Download new version
  echo -n "Downloading latest version..."
  if ! wget --quiet --output-document="$_payloadName" $UPDATE_BASE/$SELF ; then
    echo "Failed: Error while trying to wget new version!"
    echo "File requested: $UPDATE_BASE/$SELF"
    exit 1
  fi
  echo "Done."
  
  # Restore shebang
  _interpreter=$(head --lines=1 "$0")
  echo $_interpreter > "$_tempFileName"
  tail --lines=+2 "$_payloadName" >> "$_tempFileName"
  rm "$_payloadName"
  
  # Copy over modes from old version
  OCTAL_MODE=$(stat -c '%a' $SELF)
  if ! chmod $OCTAL_MODE "$_tempFileName" ; then
    echo "Failed: Error while trying to set mode on $_tempFileName."
    exit 1
  fi
  
  # Spawn update script
  cat > updateScript.sh << EOF
#!/bin/bash
# Overwrite old file with new
if mv "$_tempFileName" "$0"; then
  echo "Done."
  echo "Update complete."
  rm -- \$0
else
  echo "Failed!"
fi
EOF
  
  echo -n "Inserting update process..."
  exec /bin/bash updateScript.sh
}

Leave a Reply

You must be logged in to post a comment.