3

Is there an option to have diff (-q) not look at file contents and instead just look at size and mtime? If not, is there a tool similar to this that has the option?

Jack
  • 165

3 Answers3

6

Use rsync, but tell it not to copy or remove any files.

rsync -a -nv --delete a/ b/
1

The next script is an improvement of the answer from here:

#!/bin/sh

diffm.sh

DIFF with Modification date - a .sh (dash; bash; zsh - compatible)

"diff utility"-like script that can compare files in two directory

trees by path, size and modification date

GetOS () {

OS_kernel_name=$(uname -s)

case "$OS_kernel_name" in
    "Linux")
        eval $1="Linux"
    ;;
    "Darwin")
        eval $1="Mac"
    ;;
    "CYGWIN"*|"MSYS"*|"MINGW"*)
        eval $1="Windows"
    ;;
    "")
        eval $1="unknown"
    ;;
    *)
        eval $1="other"
    ;;
esac

}

DetectShell () { eval $1=""; if [ -n "$BASH_VERSION" ]; then eval $1="bash"; elif [ -n "$ZSH_VERSION" ]; then eval $1="zsh"; elif [ "$PS1" = '$ ' ]; then eval $1="dash"; else eval $1="undetermined"; fi }

PrintInTitle () { printf "\033]0;%s\007" "$1" }

PrintJustInTitle () { PrintInTitle "$1">/dev/tty }

trap1 () { CleanUp printf "\nAborted.\n">/dev/tty }

CleanUp () {

#Restore "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap - INT
trap - TSTP

#Restore Initial Directory:
cd "$initial_dir"

#Clear the title:
PrintJustInTitle ""

#Restore initial IFS:
#IFS=$old_IFS
unset IFS

#Set shell flags (enable globbing):
set +f

}

DisplayHelp () { printf "\n" printf "diffm - DIFF by Modification date\n" printf "\n" printf " What it does:\n" printf " - compares the files in the two provided directory tree paths (<dir_tree1> and <dir_tree2>) by:\n" printf " 1. Path\n" printf " 2. Size\n" printf " 3. Modification date\n" printf " Syntax:\n" printf " <caller_shell> '/path/to/diffm.sh' <dir_tree1> <dir_tree2> [flags]\n" printf " - where:\n" printf " - <caller_shell> can be any of the shells: dash, bash, zsh, or any other shell compatible with the &quot;dash&quot; shell syntax\n" printf " - '/path/to/diffm.sh' represents the path of this script\n" printf " - <dir_tree1> and <dir_tree2> represent the directory trees to be compared\n" printf " - [flags] can be:\n" printf " --help or -h\n" printf " Displays this help information\n" printf " Output:\n" printf " - lines starting with '<' signify files from <dir_tree1>\n" printf " - lines starting with '>' signify files from <dir_tree2>\n" printf " Notes:\n" printf " - only the files in the two provided directory tree paths are compared, not also the folders\n" printf "\n" }

Proc1 () { {
{
cd "$initial_dir" [ -n "$dir1" ] && { cd "$dir1"; PrintJustInTitle "Loading files in directory 1, please wait..."; } eval $command1 cd "$initial_dir" [ -n "$dir2" ] && { cd "$dir2"; PrintJustInTitle "Loading files in directory 2, please wait..."; } eval $command2 cd "$initial_dir" }|eval $sort_command; }|eval $uniq_command; }

Proc2 () { cd "$initial_dir" [ -n "$dir1" ] && { cd "$dir1"; PrintJustInTitle "Loading files in directory 1, please wait..."; } eval $command1 cd "$initial_dir" [ -n "$dir2" ] && { cd "$dir2"; PrintJustInTitle "Loading files in directory 2, please wait..."; } eval $command2 cd "$initial_dir" }

GetOS OS

DetectShell current_shell

OS_CASE="" if [ "$OS" = "Linux" ]; then OS_CASE="1"; # = use Linux OS commands elif [ "$OS" = "Mac" ]; then OS_CASE="2"; # = use Mac OS commands else ################################################################################# ## IN CASE YOUR OS IS NOT LINUX OR MAC: ## ## MODIFY THE NEXT VARIABLE ACCORDING TO YOUR SYSTEM REQUIREMENTS (e.g.: ## ## "1" (use Linux OS commands) or "2" (use Mac OS commands)): ## ################################################################################# OS_CASE="3" fi

if [ "$current_shell" = "undetermined" ]; then printf "\nWarning: This script was designed to work with dash, bash and zsh shells.\n\n">/dev/tty fi

#Get the program parameters into the array "params": params_count=0 for i; do params_count=$((params_count+1)) eval params_$params_count=&quot;$i&quot; done params_0=$((params_count))

if [ "$params_0" = "0" ]; then #if no parameters are provided: display help DisplayHelp CleanUp && exit 0 fi

#Create a flags array. A flag denotes special parameters: help_flag="0" i=1; j=0; while [ "$i" -le "$((params_0))" ]; do eval params_i=&quot;${params_$i}&quot; case "${params_i}" in "--help" | "-h" ) help_flag="1" ;; * ) j=$((j+1)) eval selected_params_$j=&quot;$params_i&quot; ;; esac

i=$((i+1))

done selected_params_0=$j

#Rebuild params array: for i in $(seq 1 $selected_params_0); do eval params_$i=&quot;${selected_params_$i}&quot; done params_0=$selected_params_0

if [ "$help_flag" = "1" ]; then DisplayHelp else #Run program:

error1=&quot;false&quot;
error2=&quot;false&quot;
error3=&quot;false&quot;
error4=&quot;false&quot;

{ sort --help &gt;/dev/null 2&gt;/dev/null; } || { error1=&quot;true&quot;; }
{ stat --help &gt;/dev/null 2&gt;/dev/null; } || { error2=&quot;true&quot;; }
{ find --help &gt;/dev/null 2&gt;/dev/null; } || { error3=&quot;true&quot;; }
{ uniq --help &gt;/dev/null 2&gt;/dev/null; } || { error4=&quot;true&quot;; }
if [ &quot;$error1&quot; = &quot;true&quot; -o &quot;$error2&quot; = &quot;true&quot; -o &quot;$error3&quot; = &quot;true&quot; -o &quot;$error4&quot; = &quot;true&quot; ]; then
    {
        printf &quot;\n&quot;
        if [ &quot;$error1&quot; = &quot;true&quot; ]; then printf '%s' &quot;ERROR: Could not run \&quot;sort\&quot; (necessary in order for this script to function correctly)!&quot;; fi
        if [ &quot;$error2&quot; = &quot;true&quot; ]; then printf '%s' &quot;ERROR: Could not run \&quot;stat\&quot; (necessary in order for this script to function correctly)!&quot;; fi
        if [ &quot;$error3&quot; = &quot;true&quot; ]; then printf '%s' &quot;ERROR: Could not run \&quot;find\&quot; (necessary in order for this script to function correctly)!&quot;; fi
        if [ &quot;$error4&quot; = &quot;true&quot; ]; then printf '%s' &quot;ERROR: Could not run \&quot;uniq\&quot; (necessary in order for this script to function correctly)!&quot;; fi
        printf &quot;\n&quot;
    }&gt;/dev/stderr
    exit
fi

#Check program arguments:
if [ &quot;$params_0&quot; -lt &quot;2&quot; ]; then
    printf '\n%s\n' &quot;ERROR: To few program parameters provided (expected two: &lt;dir_tree1&gt; and &lt;dir_tree2&gt;)!&quot;&gt;/dev/stderr
    exit 1
elif [ &quot;$params_0&quot; -gt &quot;2&quot; ]; then
    printf '\n%s\n' &quot;ERROR: To many program parameters provided (expected two: &lt;dir_tree1&gt; and &lt;dir_tree2&gt;)!&quot;&gt;/dev/stderr
    exit 1
fi

initial_dir=&quot;$PWD&quot; #Store initial dir

#If two program arguments are provided (&lt;dir_tree1&gt; and &lt;dir_tree2&gt;) proceed to checking them:

initial_dir=&quot;$PWD&quot; #Store initial dir
dir1=&quot;&quot;
dir2=&quot;&quot;
file1=&quot;&quot;
file2=&quot;&quot;
error_encountered=&quot;false&quot;

error1=&quot;false&quot;
error2=&quot;false&quot;
[ -e &quot;$params_1&quot; ] &amp;&amp; {
    if [ -d &quot;$params_1&quot; ]; then
        cd &quot;$params_1&quot; &gt;/dev/null 2&gt;/dev/null &amp;&amp; {
            dir1=&quot;$PWD&quot;
            cd &quot;$initial_dir&quot;
        } || {
            error1=&quot;true&quot;
        }
    elif [ ! -d &quot;$params_1&quot; ]; then
        file1=&quot;$params_1&quot;
    fi
}||{
    error2=&quot;true&quot;
}
if [ &quot;$error1&quot; = &quot;true&quot; -o &quot;$error2&quot; = &quot;true&quot; ]; then
    printf '\n%s\n' &quot;ERROR: PARAMETER1: \&quot;$params_1\&quot; does not exist as a directory/file or is not accessible!&quot;&gt;/dev/stderr
    error_encountered=&quot;true&quot;
fi

printf &quot;\n&quot;&gt;/dev/tty

error1=&quot;false&quot;
error2=&quot;false&quot;
[ -e &quot;$params_2&quot; ] &amp;&amp; {
    if [ -d &quot;$params_2&quot; ]; then
        cd &quot;$params_2&quot; &gt;/dev/null 2&gt;/dev/null &amp;&amp; {
            dir2=&quot;$PWD&quot;
            cd &quot;$initial_dir&quot;
        }||{
            error1=&quot;true&quot;
        }
    elif [ ! -d &quot;$params_2&quot; ]; then
        file2=&quot;$params_2&quot;
    fi
}||{
    error2=&quot;true&quot;
}
if [ &quot;$error1&quot; = &quot;true&quot; -o &quot;$error2&quot; = &quot;true&quot; ]; then
    printf '%s\n' &quot;ERROR: PARAMETER2: \&quot;$params_2\&quot; does not exist as a directory/file or is not accessible!&quot;&gt;/dev/stderr
    error_encountered=&quot;true&quot;
fi

if [ &quot;$error_encountered&quot; = &quot;true&quot; ]; then
    printf &quot;\n&quot;&gt;/dev/stderr
    exit
fi

## TYPE ///// PATH ///// SIZE ///// LAST TIME WRITE IN SECONDS ##

if [ &quot;$OS_CASE&quot; = &quot;1&quot; ]; then #Linux OS
    if [ -n &quot;$dir1&quot; ]; then
        command1='find . -not -type d -exec stat -c &quot;&lt; ///// %n ///// %s ///// %Y&quot; {} \;'
    else
        command1_string=&quot;$(stat -c &quot;&lt; ///// %n ///// %s ///// %Y&quot; &quot;$file1&quot;)&quot;
        command1=&quot;printf '%s\n' \&quot;\$command1_string\&quot;&quot;
    fi
    if [ -n &quot;$dir2&quot; ]; then
        command2='find . -not -type d -exec stat -c &quot;&gt; ///// %n ///// %s ///// %Y&quot; {} \;'
    else
        command2_string=&quot;$(stat -c &quot;&gt; ///// %n ///// %s ///// %Y&quot; &quot;$file2&quot;)&quot;
        command2=&quot;printf '%s\n' \&quot;\$command2_string\&quot;&quot;
    fi
    command3='date -d @'
    cd &quot;$initial_dir&quot;

    sort_command=&quot;sort -k 3&quot;

    uniq_command=&quot;uniq -u -f 2&quot;
elif [ &quot;$OS_CASE&quot; = &quot;2&quot; ]; then #Mac OS
    if [ -n &quot;$dir1&quot; ]; then
        command1='find . -not -type d -exec stat -f &quot;&lt; ///// %N ///// %z ///// %m&quot; {} \;'
    else
        command1_string=&quot;$(stat -f &quot;&lt; ///// %N ///// %z ///// %m&quot; &quot;$file1&quot;)&quot;
        command1=&quot;printf '%s\n' \&quot;\$command1_string\&quot;&quot;
    fi
    if [ -n &quot;$dir2&quot; ]; then
        command2='find . -not -type d -exec stat -f &quot;&gt; ///// %N ///// %z ///// %m&quot; {} \;'
    else
        command2_string=&quot;$(stat -f &quot;&gt; ///// %N ///// %z ///// %m&quot; &quot;$file2&quot;)&quot;
        command2=&quot;printf '%s\n' \&quot;\$command2_string\&quot;&quot;
    fi
    command3='date -j -f %s '
    cd &quot;$initial_dir&quot;

    sort_command=&quot;sort -k 3&quot;

    uniq_command=&quot;uniq -u -f 2&quot;
else
    printf '\n%s\n\n' &quot;Error: Unsupported OS!&quot;&gt;/dev/stderr
    exit 1
fi

#Trap &quot;INTERRUPT&quot; (CTRL-C) and &quot;TERMINAL STOP&quot; (CTRL-Z) signals:
trap 'trap1' INT
trap 'trap1' TSTP

old_IFS=&quot;$IFS&quot; #Store initial IFS value
IFS=&quot;
&quot;
set -f #Set shell flags (disable globbing):
found_previous=&quot;false&quot;
count=0
skip=0
if [ -n &quot;$dir1&quot; -o -n &quot;$dir2&quot; ]; then
    for line in $(\
        if [ -n &quot;$dir1&quot; -a -n &quot;$dir2&quot; ]; then\
            Proc1;\
        elif [ -z &quot;$dir1&quot; -o -z &quot;$dir2&quot; ]; then\
            Proc2;\
        fi;\
    ); do
        count=$((count+1))
        PrintJustInTitle &quot;Analyzing file $count...&quot;
        if [ -z &quot;$current_line_file_type&quot; ]; then
            current_line=&quot;$line&quot;
            current_line_file_type=&quot;${line%%&quot; ///// &quot;*}&quot;
            current_line_file_mtime_in_seconds=&quot;${line##*&quot; ///// &quot;}&quot;
            current_line_file_type_path_and_size=&quot;${line%&quot; ///// &quot;*}&quot;
            current_line_file_size=&quot;${current_line_file_type_path_and_size##*&quot; ///// &quot;}&quot;
            current_line_file_type_path=&quot;${line%&quot; ///// &quot;*&quot; ///// &quot;*}&quot;
            current_line_file_path=&quot;${current_line_file_type_path#*&quot; ///// &quot;}&quot;
        else
            previous_line=&quot;$current_line&quot;
            previous_line_file_type=&quot;$current_line_file_type&quot;
            previous_line_file_mtime_in_seconds=&quot;$current_line_file_mtime_in_seconds&quot;
            previous_line_file_type_path_and_size=&quot;$current_line_file_type_path_and_size&quot;
            previous_line_file_size=&quot;$current_line_file_size&quot;
            previous_line_file_type_path=&quot;$current_line_file_size&quot;
            previous_line_file_path=&quot;$current_line_file_path&quot;

            current_line=&quot;$line&quot;
            current_line_file_type=&quot;${line%%&quot; ///// &quot;*}&quot;
            current_line_file_mtime_in_seconds=&quot;${line##*&quot; ///// &quot;}&quot;
            current_line_file_type_path_and_size=&quot;${line%&quot; ///// &quot;*}&quot;
            current_line_file_size=&quot;${current_line_file_type_path_and_size##*&quot; ///// &quot;}&quot;
            current_line_file_type_path=&quot;${line%&quot; ///// &quot;*&quot; ///// &quot;*}&quot;
            current_line_file_path=&quot;${current_line_file_type_path#*&quot; ///// &quot;}&quot;

            if [ ! &quot;$skip&quot; = &quot;$count&quot;  ]; then
                if [ &quot;$found_previous&quot; = &quot;false&quot; ]; then
                    seconds_difference=$(($current_line_file_mtime_in_seconds - $previous_line_file_mtime_in_seconds))
                    if [ \
                        \( &quot;$current_line&quot; = &quot;$previous_line&quot; \) -o \
                        \( \
                            \( &quot;$current_line_file_path&quot; = &quot;$previous_line_file_path&quot; \) -a \
                            \( &quot;$current_line_file_size&quot; = &quot;$previous_line_file_size&quot; \) -a \
                            \( &quot;$seconds_difference&quot; = &quot;1&quot; -o &quot;$seconds_difference&quot; = &quot;-1&quot; \) \
                        \) \
                    ]; then
                        found_previous=&quot;true&quot;
                        skip=$((count+1))
                    else
                        printf '%s\n' &quot;$previous_line_file_type $previous_line_file_path - &quot;&quot;Size: &quot;&quot;$previous_line_file_size&quot;&quot; Bytes&quot;&quot; - &quot;&quot;Modified Date: &quot;&quot;$(eval $command3$previous_line_file_mtime_in_seconds)&quot;
                        found_previous=&quot;false&quot;
                    fi
                else
                    printf '%s\n' &quot;$previous_line_file_type $previous_line_file_path - &quot;&quot;Size: &quot;&quot;$previous_line_file_size&quot;&quot; Bytes&quot;&quot; - &quot;&quot;Modified Date: &quot;&quot;$(eval $command3$previous_line_file_mtime_in_seconds)&quot;
                    found_previous=&quot;false&quot;
                fi
            else
                found_previous=&quot;false&quot;
            fi
        fi
    done
    #Treat last case separately:
    if [ &quot;$count&quot; -gt &quot;0&quot; ]; then
        if [ &quot;$found_previous&quot; = &quot;false&quot; ]; then
            printf '%s\n' &quot;$current_line_file_type $current_line_file_path - &quot;&quot;Size: &quot;&quot;$current_line_file_size&quot;&quot; Bytes&quot;&quot; - &quot;&quot;Modified Date: &quot;&quot;$(eval $command3$current_line_file_mtime_in_seconds)&quot;
        fi
    fi
else
    line1=&quot;&quot;
    line2=&quot;&quot;
    for line in $(\
        if [ -n &quot;$dir1&quot; -a -n &quot;$dir2&quot; ]; then\
            Proc1;\
        elif [ -z &quot;$dir1&quot; -o -z &quot;$dir2&quot; ]; then\
            Proc2;\
        fi;\
    ); do
        if [ -z &quot;$line1&quot; ]; then
            line1=&quot;$line&quot;
            line1_file_type=&quot;${line%%&quot; ///// &quot;*}&quot;
            line1_file_mtime_in_seconds=&quot;${line##*&quot; ///// &quot;}&quot;
            line1_file_type_path_and_size=&quot;${line%&quot; ///// &quot;*}&quot;
            line1_file_size=&quot;${line1_file_type_path_and_size##*&quot; ///// &quot;}&quot;
            line1_file_type_path=&quot;${line%&quot; ///// &quot;*&quot; ///// &quot;*}&quot;
            line1_file_path=&quot;${line1_file_type_path#*&quot; ///// &quot;}&quot;
        else
            line2=&quot;$line&quot;
            line2_file_type=&quot;${line%%&quot; ///// &quot;*}&quot;
            line2_file_mtime_in_seconds=&quot;${line##*&quot; ///// &quot;}&quot;
            line2_file_type_path_and_size=&quot;${line%&quot; ///// &quot;*}&quot;
            line2_file_size=&quot;${line2_file_type_path_and_size##*&quot; ///// &quot;}&quot;
            line2_file_type_path=&quot;${line%&quot; ///// &quot;*&quot; ///// &quot;*}&quot;
            line2_file_path=&quot;${line2_file_type_path#*&quot; ///// &quot;}&quot;

            seconds_difference=$(($line2_file_mtime_in_seconds - $line1_file_mtime_in_seconds))
            if [ \
                \( &quot;$line2_file_size&quot; = &quot;$line1_file_size&quot; \) -a \
                \( \
                    \( &quot;$line2_file_mtime_in_seconds&quot; = &quot;$line1_file_mtime_in_seconds&quot; \) -o \
                    \( &quot;$seconds_difference&quot; = &quot;1&quot; -o &quot;$seconds_difference&quot; = &quot;-1&quot; \) \
                \) \
            ]; then
                :;
            else
                printf '%s\n' &quot;$line1_file_type $line1_file_path - &quot;&quot;Size: &quot;&quot;$line1_file_size&quot;&quot; Bytes&quot;&quot; - &quot;&quot;Modified Date: &quot;&quot;$(eval $command3$line1_file_mtime_in_seconds)&quot;
                printf '%s\n' &quot;$line2_file_type $line2_file_path - &quot;&quot;Size: &quot;&quot;$line2_file_size&quot;&quot; Bytes&quot;&quot; - &quot;&quot;Modified Date: &quot;&quot;$(eval $command3$line2_file_mtime_in_seconds)&quot;
            fi
        fi
    done
fi

CleanUp

fi

  • What it does:
  • Compares the files (recursively) in the two directory tree paths provided as parameters (we denote them as: <dir_tree1> and <dir_tree2>) by:
    1. relative path
    2. size
    3. modification date
  • if it finds differences: it lists the relative paths of the files that are different and their details (size and date modified):
    • relative file paths for files in <dir_tree1> are prefixed with '<'
    • relative file paths for files in <dir_tree2> are prefixed with '>'
  • Notes:
    • Only files are compared, not also directories
    • Should work on Linux OS, Mac OS - without installing any additional tools
  • I realize that you aren’t the original author of this code. But the original author seems to have left the building, and you’ve taken over, so I’ll treat you as if you were the author, and put these comments here. (1) You clearly understand what functions are. They are great for reducing redundancy. But you don’t use them well. Over and over we see commands repeated, once for dir1 and again for dir2, or for slightly different situations. When you’re doing the same thing(s) repeatedly, you should consider putting them into a function. … (Cont’d) – G-Man Says 'Reinstate Monica' Mar 13 '21 at 02:02
  • (Cont’d) …  (1b) As far as I can tell, Proc1 is basically just Proc2 | sort | uniq — so why write the code twice? (2) Similarly, the strings ///// %n ///// %s ///// %Y and ///// %N ///// %z ///// %m are present four times each; that redundancy should be eliminated. (3) Similarly, sometimes you have the same code in both branches of an if-then-else. (4) Why do you use /dev/stderr? I believe that >&2 is more portable. (5) Unix/Linux programs & scripts should not be verbose by default. IMO, the display of status should be optional (opt-in). … (Cont’d) – G-Man Says 'Reinstate Monica' Mar 13 '21 at 02:02
  • (Cont’d) …  You should definitely not do it if the script isn’t running on a tty, or if $TERM is not xterm.  (5b) And why do you have the functionality in two layers? PrintInTitle isn’t called anywhere but PrintJustInTitle; why make them two separate functions?  (5c) And why are you writing anything other than the status line to /dev/tty? Error messages should be written to stderr. (6) There are comments in the code for the obvious stuff. You should add explanations for the tricky stuff. What field is sort_command sorting? What is uniq_command doing?  … (Cont’d) – G-Man Says 'Reinstate Monica' Mar 13 '21 at 02:02
  • (Cont’d) … Why does Proc1 call sort_command and uniq_command when Proc2 doesn’t? What are line1 and line2? What can you ever get "$current_line" = "$previous_line"? What are skip and found_previous? Why do you test seconds_difference for 1 or -1 (but not 0)? And what, exactly, does the script do when you run it with one directory and one file (or two files) as arguments? (6b) And some of the conditions seem unnecessarily complex. If dir1 is non-null or dir2 is non-null, do something; otherwise, if dir1 is non-null and dir2 is non-null, do something; … (Cont’d) – G-Man Says 'Reinstate Monica' Mar 13 '21 at 02:03
  • (Cont’d) …  otherwise, if dir1 is null or dir2 is null, do something.  Under what (real-world) conditions does the second thing get done?  Under what conditions do we fall through and not do anything?  (Note that there are only three possible combinations of two Boolean values, where order doesn’t matter.)  (7) Does your script handle filenames with spaces?  (I suspect that it doesn’t; I’m not going to run it on my system until I understand it better.)  (8) Have you considered processing the argument list once, and not copying it into an array-that-isn’t-an-array?  … (Cont’d) – G-Man Says 'Reinstate Monica' Mar 13 '21 at 02:03
  • (Cont’d) …  (9) You have a lot of unneeded backslashes; for example, in Proc1. (10) You should try to use eval a *lot* less. (11) You broke the IFS assignment — your code now sets it to . (12) The only thing in CleanUp that needs to be there is PrintJustInTitle — everything else is shell-local settings that go away when the shell exits. (13) You should avoid using [-a] and [-o]. (14) You shouldn’t use -not in find unless you’re sure that it’s GNU find. (15) This answer would be better if it showed some example output. – G-Man Says 'Reinstate Monica' Mar 13 '21 at 02:03
  • Hello, I am the original author of both: https://unix.stackexchange.com/a/621962/456983 and https://unix.stackexchange.com/a/638761/456983 (this), but I was just trying to help, if the code doesn't fit you simply don't use it... –  Mar 15 '21 at 13:11
0

Not with diff - you don't need to look into the files for that - but just compare that information with stat in shell, as in

if [[ $(stat -c%s_%Y file1) == $(stat -c%s_%Y file2) ]]
then echo equal
else echo different
fi

The stat command provides information from the file's inode, -c allows you to select the desired attributes (%s and %Y in your case).

Janis
  • 14,222
  • I'm looking to get a picture of the way the directory trees differ including missing files/folders at any point in the structure (similar to what diff show). – Jack Apr 21 '15 at 17:07
  • 2
    Then please adjust your question accordingly, any make also clear why diff -r (recursive diff) is not helpful in your case. Also provide input and an desired output sample, so that it's clear what you expect. – Janis Apr 21 '15 at 17:11
  • diff -q -r does not work because I want to save time if the files have the same place in the path, name and mtime. – Jack Apr 21 '15 at 17:23