#!/bin/bash function helpexit() { echo 'Create or append md5/sha* list of files Usage: Creating hashes: hash-update [-r] [-t hash-type] [-f list_name] [[files to hash]] Checking hashes: hash-update -c [-f list_name] -r recursive -c check -t hash-type defaults to sha256. choose from md5, sha1, sha256, sha512 etc.. -f list_name is [type]sums.txt by default -i individual [filename].[hash] files, instead of -f list of files. If files to check omitted, list files in current folder Check files with "sha256sum -c sha256sums.txt", or passing -c to this script. To update hashes, simply delete the sha256sums file. ' exit } function valid_type() { local valid for valid in b2 md5 sha1 sha224 sha256 sha384 sha512; do if [[ "$1" = "$valid" ]]; then return 0 fi done return 1 } recursive=0 check=0 hashtype=sha256 individual=0 paths=() for ((i=1; i<=${#@}; i++)) { j=$(( i + 1 )) [[ "${!i}" = "-h" ]] && helpexit [[ "${!i}" = "--help" ]] && helpexit [[ "${!i}" = "-r" ]] && { recursive=1; continue; } [[ "${!i}" = "-c" ]] && { check=1; continue; } [[ "${!i}" = "-i" ]] && { individual=1; continue; } [[ "${!i}" = "-t" ]] && { hashtype="${!j}"; i=$(( $i + 1 )); continue; } [[ "${!i}" = "-f" ]] && { list_name="${!j}"; i=$(( $i + 1 )); continue; } paths+=("${!i}") } valid_type "$hashtype" || { echo Type "$hashtype" is not valid. echo Choose from b2 md5 sha1 sha224 sha256 sha384 sha512 exit 1 } if [[ -z "$list_name" ]]; then list_name="${hashtype}sums.txt" fi SUMBIN="${hashtype}sum" which $SUMBIN &>/dev/null || { echo $SUMBIN not in PATH exit 1 } if [[ $check -eq 1 ]]; then $SUMBIN -c "$list_name" exit $? fi if [[ ${#paths[@]} -eq 0 ]]; then paths+=(".") fi which pv &>/dev/null && PVBIN=$( which pv ) set -e if [[ $individual = 0 ]]; then echo "Updating: $list_name" test -f "$list_name" || touch "$list_name" fi updates_done=0 _add_single() { size=$( stat -c %s "$1" ) if [[ $size -gt 100000000 ]] && [[ -n "$PVBIN" ]]; then # Use pv is size > 100MB touch "$2" sum=$( $PVBIN "$1" | $SUMBIN | awk '{ print $1 }' ) printf "%s %s\n" "$sum" "$1" >> "$2" else $SUMBIN "$1" >> "$2" fi } _update() { if [ "$1" = "$list_name" ]; then return fi if [[ -L "$1" ]]; then echo "$1 is a symlink" return fi if test -d "$1"; then echo "$1 is a directory" return fi # individual checksum files if [[ $individual = 1 ]]; then if [[ "$1" = *.$hashtype ]]; then return fi if [[ -e "$1.$hashtype" ]]; then return fi _add_single "$1" "$1.$hashtype" return fi # sum list file if test -f "$1"; then if sed 's,^[^ ]\+ ,,' "$list_name" | grep -qFx "$1"; then echo "Exists: $1" else echo "New: $1" _add_single "$1" "$list_name" updates_done=1 fi fi } shopt -s globstar shopt -s nullglob for ((i=0; i<=${#paths[@]}; i++)) { if [[ -d "${paths[$i]}" ]]; then pathname="${paths[$i]}" pathname="${pathname%%/}" if [[ "$recursive" -eq 1 ]]; then for file in "${pathname}"/**; do file="${file#./}" _update "$file" done else for file in "${pathname}"/*; do file="${file#./}" _update "$file" done fi fi if [[ -f "${paths[$i]}" ]]; then file="${paths[$i]}" file="${file#./}" _update "$file" fi } if [[ $updates_done -eq 1 ]]; then sort -k 2 -o "$list_name" "$list_name" fi