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
}
