The Shell Corner: cmptree

This month's winner is Ives Aerts, who submitted cmptree.


August 23, 2001
URL:http://www.drdobbs.com/the-shell-corner-cmptree/199103123

February 2001

The Shell Corner: cmptree

Ed Schaefer

Convert Revisited

Last month, Gerard van Wageningen submitted convert, a Korn script to display numbers in binary, decimal, hexadecimal, and base 36. After my deadline, Gerard submitted a change, adding base 64. If anyone desires this change, please email me and I'll send it to you.

Directory Comparison with cmptree

This month, Ives Aerts submits cmptree, a bash script that compares the contents of two directories and reports if like-named files are different. It also reports a missing file or directory as not existing. Simply execute:

cmptree directory1 directory2
Comparing directories is not new. SCO Unix has provided shell script dircmp since before System V, and Solaris also provides a version of dircmp. For Unix variants that dont have a directory compare, such as RedHat Linux 6.1, cmptree is a worthy addition.

While dircmp executes a find on each directory comparing contents, cmptree elegantly compares the two directories recursively. The simplicity of cmptree eliminates some of the functionality of dircmp.

cmptree Limitations

cmptree reports only if files are different -- not what the differences are.

cmptree only compares normal files. Special files, such as block, character, named pipe, semaphore, and shared memory are marked as "unknown."

Also, cmptree uses test -e to check whether an object exists; thus, no read permissions causes an "object does not exist" error.

Soft links report only if they are different. For example, two directories exist: /tmp/dir1 and /tmp/dir2. In the present working directory, create two soft links:

ln -s /tmp/dir1 linkdir1
ln -s /tmp/dir2 linkdir2
Executing the following:

cmptree /tmp/dir1 /tmp/dir2
performs a valid search, but executing:

cmptree linkdir1 linkdir2
displays a "different link targets" message.

Bash Versus Korn Shell

cmptree is a bash shell script, but removing the function keyword in each function allows Korn shell execution.

In Ives' original submission, cmptree's comparefile function performed compares with:

cmp $1 $2

While the command above works fine in the bash shell, in the Korn, the file name does not list. I changed the cmp command to return an exit code, sampling the value as such:

  cmp -s $1 $2
  if [ $? -gt 0 ]; then
    echo "$1 different from $2"
  fi
The code snippet above works in both the bash and Korn shells.

#!/bin/bash
#
# cmptree: compare directory trees recursively and report the differences.
# Author: Ives Aerts

function gettype () {
  if [ -L $1 ]; then
    echo "softlink"
  elif [ -f $1 ]; then
    echo "file"
  elif [ -d $1 ]; then
    echo "directory"
  else
    echo "unknown"
  fi
}

function exists () {
  if [ -e $1 -o -L $1 ]; then
    return 0;
  else
    echo "$1 does not exist."
    return 1;
  fi
}

function comparefile () {
  cmp -s $1 $2
  if [ $? -gt 0 ]; then
    echo "$1 different from $2"
#  else
#    echo "$1 same as $2"
  fi
  return
}

function comparedirectory () {
  local result=0
  for i in `(ls -A $1 && ls -A $2) | sort | uniq`; do
    compare $1/$i $2/$i || result=1
  done
  return $result
}

function comparesoftlink () {
  local dest1=`ls -l $1 | awk '{ print $11 }'`
  local dest2=`ls -l $2 | awk '{ print $11 }'`

  if [ $dest1 = $dest2 ]; then
    return 0
  else
    echo "different link targets $1 -> $dest1, $2 -> $dest2"
    return 1
  fi
}

# compare a file, directory, or softlink
function compare () {
  (exists $1 && exists $2) || return 1;

  local type1=$(gettype $1)
  local type2=$(gettype $2)

  if [ $type1 = $type2 ]; then
    case $type1 in
      file)
        comparefile $1 $2
        ;;
      directory)
        comparedirectory $1 $2
        ;;
      softlink)
        comparesoftlink $1 $2
        ;;
      *)
        echo "$1 of unknown type"
        false
        ;;
    esac
  else
    echo "type mismatch: $type1 ($1) and $type2 ($2)."
    false
  fi

  return
}

if [ 2 -ne $# ]; then
cat << EOU
Usage: $0 dir1 dir2
Compare directory trees:
  files are binary compared (cmp)
  directories are checked for identical content
  soft links are checked for identical targets
EOU
  exit 10
fi

compare $1 $2
exit $?

Check out past winners of shell corner!!!

By day, Ed Schaefer is a mild-mannered senior programmer-analyst for Intel's Factory Integrated Information Systems (FIIS). The standard employer-employee disclaimer is in effect: In this forum, Ed doesn't speak for Intel and his views on Unix and all other topics are his own.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.