#!/bin/bash # ------------------------------------------------------------------- # Copy the SOURCE (if it exists) onto TARGET # # SOURCE ----> TARGET # # - Nothing is copied if the files do not differ (you can specify # that it shall not matter if blank lines and space has been # added or removed or if there are differences in lines that # start with '#' using the argument 'relaxedcompare'.) # - If the target exists and differ, a savefile is created first # (the creation of the savefile can be suppressed by passing the # NOSAVEFILE flag, see below.) # - The string %COPYMARK% in the source file is replaced by an # appropriate comment saying that the target is file copied from # the source at time x. # - Permissions, owner and group can be set by passing appropriate # additional arguments after the source and target arguments. # The case of these additional arguments does not matter, and # neither does the order in which they appear: # # 'nosavefile' -- do not create a savefile # 'verbose -- be verbose # 'relaxedcompare' -- disregard whitespace changes and comments # when comparing source and target # 'mode=DDD -- set mode of target to DDD (e.g. 644) # 'user=UID -- set user of target to UID or username # 'group=GID -- set group of target to GID or groupname # 'now=NOW -- set the ending of the savefile to NOW # instead of a string derived from the # current date # ------------------------------------------------------------------- function copyOnto { local SOURCE=$1 local TARGET=$2 if [[ -z "$SOURCE" || -z "$TARGET" ]]; then echo "Function not called with 'SOURCE' (first arg) and 'TARGET' (second arg) -- exiting" >&2 exit 1 fi shift; shift # # The remainder of the args are flags # local RELAXEDCOMPARE= local NOSAVEFILE= local MODE=644 # default mode for target local USER= local GROUP= local VERBOSE= local NOW=`date '+%Y%m%d.%H%M%S'` # default NOW string # # Loop over those args # while [[ -n $1 ]]; do local VAL=`echo $1 | tr '[:upper:]' '[:lower:]'` if [[ $VAL == 'nosavefile' ]]; then NOSAVEFILE=YES shift;continue fi if [[ $VAL == 'verbose' ]]; then VERBOSE=YES shift;continue fi if [[ $VAL == 'relaxedcompare' ]]; then RELAXEDCOMPARE=YES shift;continue fi local NEWMODE=`echo $VAL | perl -e '$line=<>; $line=~/^mode=(\d\d\d)$/; print $1;'` if [[ -n $NEWMODE ]]; then MODE=$NEWMODE shift;continue fi local NEWUSER=`echo $VAL | perl -e '$line=<>; $line=~/^user=(\w+)$/; print $1;'` if [[ -n $NEWUSER ]]; then USER=$NEWUSER shift;continue fi local NEWGROUP=`echo $VAL | perl -e '$line=<>; $line=~/^group=(\w+)$/; print $1;'` if [[ -n $NEWGROUP ]]; then GROUP=$NEWGROUP shift;continue fi local NEWNOW=`echo $VAL | perl -e '$line=<>; $line=~/^now=(\w+)$/; print $1;'` if [[ -n $NEWNOW ]]; then NOW=$NEWNOW shift;continue fi echo "Could not parse argument '$1' -- not considering it" >&2 shift done # # Printout if verbose # if [[ -n $VERBOSE ]]; then echo "Source = '$SOURCE'" >&2 echo "Target = '$TARGET'" >&2 if [[ -n $NOSAVEFILE ]]; then echo " Nosavefile is on" >&2; fi if [[ -n $RELAXEDCOMPARE ]]; then echo " Relaxedcompare is on" >&2; fi if [[ -n $USER ]]; then echo " User is $USER" >&2; fi if [[ -n $GROUP ]]; then echo " Group is $GROUP" >&2; fi if [[ -n $MODE ]]; then echo " Mode is $MODE" >&2; fi echo " Now is '$NOW'" >&2 fi # # Source must exist and be a file (a symlink also passes -f) # if [[ ! -f "$SOURCE" ]]; then echo "Source '$SOURCE' does not exist -- doing nothing" >&2 return fi # # Clean up if target is a symlink; this may not always be what is wanted though # if [[ -L "$TARGET" ]]; then echo "Destination '$TARGET' is a symlink - deleting it first" >&2 /bin/rm "$TARGET" fi # # Do nothing if same file or no differences # local DIFFOPTIONS= if [[ -n $RELAXEDCOMPARE ]]; then # Note that the argument to ignore-matching-lines has no quotes (after eval, diff would be looking for them) DIFFOPTIONS="--ignore-space-change --ignore-blank-lines --ignore-matching-lines=^#" fi if [[ -f "$TARGET" ]]; then if [[ `stat --format=%i "$SOURCE"` == `stat --format=%i "$TARGET"` ]]; then echo "Source file '$SOURCE' and target file '$TARGET' have the same inode number -- doing nothing" >&2 return fi # # Do nothing if no differences # /usr/bin/diff $DIFFOPTIONS "$SOURCE" "$TARGET" >/dev/null 2>&1 local RES=$? if [[ $RES==0 ]]; then echo "Source file '$SOURCE' and target file '$TARGET' have identical contents -- doing nothing" >&2 return fi fi # # Destination file does not exist or exists and differs. # if [[ -f "$TARGET" ]]; then if [[ -z $NOSAVEFILE ]]; then SAVEFILE=${TARGET}_${NOW} echo "Source file '$SOURCE' and already-existing target file '$TARGET' differ; saving existing file to '$SAVEFILE'" >&2 /bin/mv $TARGET $SAVEFILE if [[ $? != 0 ]]; then echo "Could not rename '$TARGET' -- exiting" >&2 exit 1 fi else echo "A target file '$TARGET' exists and differs from the source file '$SOURCE'; replacing the target file" >&2 echo "---------------------" /usr/bin/diff --side-by-side $DIFFOPTIONS "$SOURCE" "$TARGET" echo "---------------------" fi fi # # Copy onto non-existing or differring file # /bin/cp "$SOURCE" "$TARGET" if [[ $? != 0 ]]; then echo "Could not copy to target file '$TARGET' -- exiting" >&2 exit 1 fi # # Set modus # /bin/chmod "$MODE" "$TARGET" if [[ $? != 0 ]]; then echo "Could not chmod target file '$TARGET' to '$MODE' -- exiting" >&2 exit 1 fi # # Change group # if [[ -n "$GROUP" ]]; then /bin/chgrp "$GROUP" "$TARGET" if [[ $? != 0 ]]; then echo "Could not set target file '$TARGET' to group '$GROUP' -- exiting" >&2 exit 1 fi fi # # Change owner # if [[ -n "$USER" ]]; then /bin/chown "$USER" "$TARGET" if [[ $? != 0 ]]; then echo "Could not set target file '$TARGET' to user '$USER' -- exiting" >&2 exit 1 fi fi # # Replace the string '%COPYMARK%' in target # local WHEN=`date` local RVAL=`echo "Copied from '${SOURCE}' by '${0}' at '${WHEN}'" | sed 's,/,\\\\/,g'` /bin/sed --in-place "s/%COPYMARK%/${RVAL}/" "${TARGET}" echo "Target file '$TARGET' successfully installed from source file '$SOURCE'" >&2 }