#!/bin/bash usage() { echo "USAGE: $0 [--clobber=none|first|second] [--verbose] [--] search_dir other_dir" } verbose= debug() { if [ -n "$verbose" ] then echo "$@" fi } clobber=none while [ "${1:0:2}" == "--" ] do case $1 in --) break ;; --verbose) verbose=true ;; --clobber=none) ;; --clobber=first) clobber=first ;; --clobber=second) clobber=second ;; *) >&2 echo "Unrecognized option: $1" usage exit 1 esac shift done if (( $# != 2 )) then usage exit 0 fi from="$1" to="$2" hardlink-file-if-duplicate() { if [ ! -f "$1" ] then debug "Not a regular file (or does not exist): $1" return elif [ ! -f "$2" ] then debug "Not a regular file (or does not exist): $2" return elif [ "$1" -ef "$2" ] then debug "Already a hardlink: $1 => $2" return elif ! cmp -s -- "$1" "$2" then debug "Files aren't identical: $1 != $2" return fi # from https://unix.stackexchange.com/a/120866 if [ "$(stat --format="%h" "$2")" -gt 1 ] then if [ "$(stat --format="%h" "$1")" -gt 1 ] then case $clobber in none) echo "Skipping: both files have multiple hardlinks. Specify --clobber=first to delete $1 or --clobber=second to delete $2." return ;; first) echo "Both files have multiple hardlinks, defaulting to $2 overwritting $1" one_link_file="$1" other_file="$2" ;; second) echo "Both files have multiple hardlinks, defaulting to $1 overwritting $2" one_link_file="$2" other_file="$1" ;; esac else one_link_file="$1" other_file="$2" fi else one_link_file="$2" other_file="$1" fi ln -v -f -- "$other_file" "$one_link_file" } if [ "$from" -ef "$to" ] then echo "WARNING: $from and $to are the same path. Exiting." exit 2 elif [ ! -d "$from" ] || [ ! -d "$to" ] then debug hardlink-file-if-duplicate "$from" "$to" hardlink-file-if-duplicate "$from" "$to" exit fi if [[ "${from:0-1}" != "/" ]] then from="$from/" fi if [[ "${to:0-1}" != "/" ]] then to="$to/" fi debug "Searching $from ..." # from https://stackoverflow.com/a/9612232 find "$from" -type f -print0 | while IFS= read -r -d '' line; do debug hardlink-file-if-duplicate "$line" "${line/#$from/$to}" hardlink-file-if-duplicate "$line" "${line/#$from/$to}" done