#!/bin/bash version="0.34" program=$(basename $0) NEW="" # If there are more than $NEW % new lines, skip update OLD="" # If there are more than $OLD % deleted lines, skip update FILE="" verbose="" silent="" # ---------------------------------------------------------------------- function usage() { cat < EOF exit } # ---------------------------------------------------------------------- function note() { if [ -n "$verbose" ]; then echo -e "$program: $*" fi } # ---------------------------------------------------------------------- function warn() { [ "$QUIET" ] || echo -e "$program: $*" } # ---------------------------------------------------------------------- function err() { echo -e "$program: $*" > /dev/stderr } # ----------------------------------------------------------------------------- function leave() { errcode=$1; shift if [ "$errcode" -ge "2" ]; then warn "$*"; else note "$*"; fi if [ -f "$tempfile" ]; then rm $tempfile; fi if [ -f "$difffile" ]; then rm $difffile; fi if [ "$errcode" = "1" -a "$RESULT" = "0" ]; then exit 0; else exit $errcode; fi } # ---------------------------------------------------------------------- # Set up error trap trap 'leave 127 "$program($LINENO): Command failed with error code $? while processing '$origfile'."' ERR # exit with a message if a command fails set -e # ---------------------------------------------------------------------- # Get any options # # Default values PAT="\$path\$base.%Y-%m-%d_%H%M" RESULT="0" QUIET="" # Based on the sample code in /usr/share/doc/util-linux/examples/parse.bash.gz if [ "$(uname)" = "Linux" ]; then GETOPT_RESULT=$(getopt -o bc:ef:hn:o:p:qrvV --long backup,maxchg:,empty,file:,help,maxnew:,maxold:,prefix:,report,quiet,verbose,version -n "$program" -- "$@") else GETOPT_RESULT=$(getopt bc:ef:hn:o:p:qrvV "$@") fi if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi note "GETOPT_RESULT: $GETOPT_RESULT" eval set -- "$GETOPT_RESULT" while true ; do case "$1" in -b|--backup) backup=1; shift ;; # Back up earlier versions by creating a backup file -c|--maxchg) CHG="$2"; shift 2 ;; # Limit on percentage of changed lines -e|--empty) empty=1; shift ;; # Permit the update to be empty (default: discard) -f|--file) FILE="$2"; shift 2 ;; # Read input from FILE instead of standard input -h|--help) usage; shift ;; # Show this text and exit -n|--maxnew) NEW="$2"; shift 2 ;; # Limit on percentage of new (added) lines -o|--maxold) OLD="$2"; shift 2 ;; # Limit on percentage of old (deleted) lines -p|--pat*) PAT="$2"; shift 2 ;; # Backup name base ('$path$base.%Y%m%d_%H%M') -q|--quiet) QUIET=1; shift;; # Be less verbose -r|--result) RESULT=1; shift ;; # Return 1 if update not done -v|--verbose) verbose=1; shift ;; # Be more verbose about what's happening -V|--version) echo -e "$program\t$version"; exit;; # Show version and exit --) shift ; break ;; *) echo "$program: Internal error, inconsistent option specification." ; exit 1 ;; esac done if [ $CHG ]; then OLD=$CHG; NEW=$CHG; fi if [ $# -lt 1 ]; then echo -e "$program: Missing output filename\n"; usage; fi origfile=$1 tempfile=$(mktemp) difffile=$(mktemp) if [ -e "$origfile" ]; then cp -p $origfile $tempfile # For ownership and permissions cat $FILE > $tempfile [ "$FILE" ] && touch -r $FILE $tempfile # This won't work if we don't have sufficient privileges: #chown --reference=$origfile $tempfile #chmod --reference=$origfile $tempfile else cat $FILE > $origfile [ "$FILE" ] && touch -r $FILE $tempfile leave 0 "Created file '$origfile'" fi origlen=$(wc -c < $origfile) newlen=$(wc -c < $tempfile) if [ $origlen = 0 -a $newlen = 0 ]; then rm $tempfile leave 1 "New content is identical (and void) - not updating '$origfile'." fi if [ $newlen = 0 -a -z "$empty" ]; then leave 1 "New content is void - not updating '$origfile'." fi diff $origfile $tempfile > $difffile || [ $? -le 1 ] && true # suppress the '1' error code on differences difflen=$(wc -l < $difffile) if [ $difflen = 0 ]; then leave 1 "New content is identical - not updating '$origfile'." fi if [ "$OLD" -o "$NEW" ]; then if [ "$NEW" ]; then maxnew=$(( $origlen * $NEW / 100 )); fi if [ "$OLD" ]; then maxdel=$(( $origlen * $OLD / 100 )); fi newcount=$(grep "^> " $difffile | wc -c) outcount=$(grep "^< " $difffile | wc -c) delcount=$(grep "^! " $difffile | wc -c) delcount=$(( $outcount + $delcount )) rm $difffile if [ "$OLD" ]; then if [ "$delcount" -ge "$maxdel" ]; then cp $tempfile $origfile.update leave 2 "New content has too many removed lines ($delcount/$origlen)\n - not updating '$origfile'.\nNew content placed in '$origfile.update' instead" fi fi if [ "$NEW" ]; then if [ "$newcount" -ge "$maxnew" ]; then cp $tempfile $origfile.update leave 2 "New content has too many added lines ($newcount/$origlen)\n - not updating '$origfile'.\nNew content placed in '$origfile.update' instead" fi fi fi if [ "$backup" ]; then path=${origfile%/*} name=${origfile##*/} base=${name%.*} ext=${origfile##*.} if [ "$ext" = "$origfile" ]; then ext="" elif [ ! "${ext%/*}" = "$ext" ]; then ext="" else ext=".$ext" fi if [ "$path" = "$origfile" ]; then path="" else path="$path/" fi ver=1 backfile=$(eval date +"$PAT") backpath="${backfile%/*}" if [ "$backpath" = "$backfile" ]; then backpath="." fi if [ ! -d $backpath ]; then if [ -e $backpath ]; then leave 3 "The backup path '$backpath' exists but isn't a directory" else mkdir -p $backpath fi fi while [ -e "$backfile,$ver$ext" ]; do ver=$(( $ver+1 )) done note "Saving backup: $backfile,$ver$ext" cp -p "$origfile" "$backfile,$ver$ext" chmod -w "$backfile,$ver$ext" || true fi if ! mv $tempfile $origfile; then cp -p $tempfile $origfile; fi leave 0 "Updated file '$origfile'"