#!/bin/bash # Copyright 2013 Norman Carver # # Bash shell script to compare two files, one local and the second remote, # using sdiff to determine whether they are identical or they differ. # # Syntax: # sdiff-remote [(-q | -d | -v)] [-f] LOCAL_FILE_PATH ( [USERNAME@]HOSTNAME:REMOTE_FILE_PATH | [USERNAME@]HOSTNAME: ) # # Output can be controlled with -q, -d, or -v options: # default -- print same/difference message # -q -- quiet, suppress all output # -d -- if different, sdiff only differences # -v -- if different, sdiff entire files # # -f (fast) option causes files to be compared by size only, use for large files. # Default is to compare files using checksums. # # Exit status is success if files match, failure if they differ. # # Example calls: # sdiff-remote Documents/instructions.text carver@remhost.example:/backup/files/diff.txt # sdiff-remote Documents/instructions.text carver@remhost.example:Documents/instructions.text # sdiff-remote Documents/instructions.text carver@remhost.example: # sdiff-remote Documents/instructions.text remhost.example: # # Requires: rsync (local machine) # ssh (local machine) # SSH server running on remote machine # # Version history: # 1.0 (07/28/2013) Initial public release # Copyright 2013 Norman Carver # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at # your option) any later version. # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . function print_usage() { echo "Usage: sdiff-remote [(-q | -d | -v)] [-f] LOCAL_FILE_PATH ( [USERNAME@]HOSTNAME:REMOTE_FILE_PATH | [USERNAME@]HOSTNAME: )" echo "(-q for quiet/no output, -d for sdiff differences, -v for verbose sdiff, -f for fast check using only file size)" } # Check that number of arguments was valid: if [[ $# -lt 2 ]]; then print_usage exit 1 fi # Decode options: quiet=false diff=false verbdiff=false fast=false while getopts ":dfqv" opt; do case $opt in d) diff=true ;; f) fast=true ;; q) quiet=true ;; v) verbdiff=true ;; \?) print_usage exit 1 ;; esac done shift $((OPTIND-1)) if $quiet && ( $diff || $verbdiff ); then print_usage exit 1 fi # Decode arguments: filelocal=$1 remotespec=$2 # Determine the remote host and file: if [[ ! ("$remotespec" =~ .+:.*) ]]; then echo "No \"HOSTNAME:\"" print_usage exit 1 fi remotehost=${remotespec%%:*} if [[ "$remotespec" =~ .+:$ ]]; then #Given hostname only for remote file spec: if [[ "$filelocal" =~ /.+ ]]; then # Absolute local path, so: fileremote=$filelocal else # Relative local path, so: fileremote=${PWD}/${filelocal} fi else fileremote=${remotespec#*:} fi # Verify the file arguments: error=false if [[ ! -f "$filelocal" ]]; then echo "Invalid LOCAL_FILE_PATH: $filelocal" print_usage exit 1 fi if ! ssh "${remotehost}" "ls \"${fileremote}\" &> /dev/null"; then echo "Invalid remote file: $remotehost:$fileremote" print_usage exit 1 fi # Find out if the local and remote files differ: if $fast; then rsyncoutput=$(rsync --dry-run --size-only --itemize-changes "${filelocal}" "${remotehost}:${fileremote}" 2> /dev/null) else rsyncoutput=$(rsync --dry-run --checksum --itemize-changes "${filelocal}" "${remotehost}:${fileremote}" 2> /dev/null) fi if [[ $? != 0 ]]; then echo "rsync failed: \"${filelocal}\" vs. \"${remotehost}:${fileremote}\"" exit 1 fi if [[ -z "$rsyncoutput" ]]; then #The files are identical: if ! $quiet; then echo "The files are identical."; fi exit 0 else #The files differ: if ! $quiet; then echo "The files DIFFER!"; fi if $verbdiff; then echo ssh "${remotehost}" "cat ${fileremote}" | sdiff ${sdiffoptions} "${filelocal}" - elif $diff; then echo ssh "${remotehost}" "cat ${fileremote}" | sdiff --suppress-common-lines ${sdiffoptions} "${filelocal}" - fi exit 1 fi # EOF