#!/bin/sh help() { cat << HELPDOC usage: $(basename "$0") [options] [file]... OPTIONS: -h, --help show this menu -c, --check read and verify checksums from file -r, --recursive produce a checksum for all files in a directory recursively --recursive-sum produce a checksum for a directory. sensitive to filenames --recursive-sum-nopath produce a checksum for a directory. not sensitive to filenames/placement -s, --silent produce no output to stderr -v, --verbose output a progress bar of hash progress if pv is avaliable (wip) MODES: -S, --sha use sha1sum for checksums -M, --md5 use md5sum for checksums -X, --xxh use xxhsum for checksums (default if avaliable with sha1sum as fallback) HELPDOC } # set defaults cat="pv -F'%r_[%b]_[%t]_[%e]_%p'" ! command -v pv >/dev/null && cat=cat hash=xxhsum ! command -v xxhsum >/dev/null && hash=sha1sum hashfiles="" verbose=1 recursive="" outputfile="" while true; do case "$1" in -c|--check) [ -n "$2" ] && hashfiles="$hashfiles $2" && shift ;; -s|--silent) verbose=0 ;; -v|--verbose) verbose=2 ;; --recursive|-r) recursive=3 ;; --recursive-sum) recursive=2 ;; --recursive-sum-nopath) recursive=1 ;; -S|--sha) hash=sha1sum ;; -M|--md5) hash=md5sum ;; -X|--xxh) hash=xxhsum ;; -h|--help) help; exit 0 ;; -*) help; exit 1 ;; *) break ;; esac shift done case $verbose in 0) cat=cat ;; 1) cat=cat ;; esac # run hash while [ -n "$1" ]; do if [ -d "$1" ]; then # is directory # check if using -r flag case "$recursive" in 1) # get hash of all files, sort hashes, hash hashes # TODO make pv work in this mode #catd="$cat" #if [ $verbose -ge 2 ] && [ "$catd" != "cat" ]; then # total_files=$(du -a "$1" | wc -l) # catd="pv -l -s$total_files -F'[%b/$total_files]_[%t]_[%e]_%p'" #fi #sum=`find "$1" -type f -print0 | xargs -r0 $hash | $catd | cut -d' ' -f1 | sort | $hash | cut -d' ' -f1` 2>/dev/null sum=`find "$1" -type f -print0 | xargs -r0 $hash | cut -d' ' -f1 | sort | $hash | cut -d' ' -f1` 2>/dev/null echo "r/$sum $1" ;; 2) # hash sorted filenames # sort all filenames w/ full path and hash # hash those sums together # `tar -c -f - "$1" | $hash` is a good idea but it doesn't work since changes to the fs affect tar filehash=`find "$1" -type f -print0 | sort -z | xargs -r0 $cat | $hash | cut -d' ' -f1` pathhash=`find "$1" -printf "%P\0" | sort -z | $hash | cut -d' ' -f1` sum=`printf "$filehash$pathhash" | $hash | cut -d' ' -f1` echo "p/$sum $1" ;; 3) # sort files and hash each one in sequence # TODO make pv work in this mode find "$1" -type f -print0 | sort -z | xargs -r0 $hash ;; *) [ $verbose -ge 1 ] && echo "-r not specified for directory $1. Exiting" exit 1 ;; esac else # is file if ! [ -f "$1" ]; then [ $verbose -gt 1 ] && echo "File $1 does not exist. Exiting" exit 1 fi files="$1" while [ -f "$2" ]; do files="$files\0$2" shift done printf "$files" | xargs -r0 $hash fi shift done