是否有一个选项可以让 diff (-q) 不查看文件内容,而只查看大小和 mtime?如果没有,是否有类似的工具可以提供此选项?
答案1
使用rsync
,但告诉它不要复制或删除任何文件。
rsync -a -nv --delete a/ b/
答案2
下一个脚本是对答案的改进这里:
#!/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 \"dash\" 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=\"\$i\"
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=\"\$\{params_$i\}\"
case "${params_i}" in
"--help" | "-h" )
help_flag="1"
;;
* )
j=$((j+1))
eval selected_params_$j=\"\$params_i\"
;;
esac
i=$((i+1))
done
selected_params_0=$j
#Rebuild params array:
for i in $(seq 1 $selected_params_0); do
eval params_$i=\"\$\{selected_params_$i\}\"
done
params_0=$selected_params_0
if [ "$help_flag" = "1" ]; then
DisplayHelp
else #Run program:
error1="false"
error2="false"
error3="false"
error4="false"
{ sort --help >/dev/null 2>/dev/null; } || { error1="true"; }
{ stat --help >/dev/null 2>/dev/null; } || { error2="true"; }
{ find --help >/dev/null 2>/dev/null; } || { error3="true"; }
{ uniq --help >/dev/null 2>/dev/null; } || { error4="true"; }
if [ "$error1" = "true" -o "$error2" = "true" -o "$error3" = "true" -o "$error4" = "true" ]; then
{
printf "\n"
if [ "$error1" = "true" ]; then printf '%s' "ERROR: Could not run \"sort\" (necessary in order for this script to function correctly)!"; fi
if [ "$error2" = "true" ]; then printf '%s' "ERROR: Could not run \"stat\" (necessary in order for this script to function correctly)!"; fi
if [ "$error3" = "true" ]; then printf '%s' "ERROR: Could not run \"find\" (necessary in order for this script to function correctly)!"; fi
if [ "$error4" = "true" ]; then printf '%s' "ERROR: Could not run \"uniq\" (necessary in order for this script to function correctly)!"; fi
printf "\n"
}>/dev/stderr
exit
fi
#Check program arguments:
if [ "$params_0" -lt "2" ]; then
printf '\n%s\n' "ERROR: To few program parameters provided (expected two: <dir_tree1> and <dir_tree2>)!">/dev/stderr
exit 1
elif [ "$params_0" -gt "2" ]; then
printf '\n%s\n' "ERROR: To many program parameters provided (expected two: <dir_tree1> and <dir_tree2>)!">/dev/stderr
exit 1
fi
initial_dir="$PWD" #Store initial dir
#If two program arguments are provided (<dir_tree1> and <dir_tree2>) proceed to checking them:
initial_dir="$PWD" #Store initial dir
dir1=""
dir2=""
file1=""
file2=""
error_encountered="false"
error1="false"
error2="false"
[ -e "$params_1" ] && {
if [ -d "$params_1" ]; then
cd "$params_1" >/dev/null 2>/dev/null && {
dir1="$PWD"
cd "$initial_dir"
} || {
error1="true"
}
elif [ ! -d "$params_1" ]; then
file1="$params_1"
fi
}||{
error2="true"
}
if [ "$error1" = "true" -o "$error2" = "true" ]; then
printf '\n%s\n' "ERROR: PARAMETER1: \"$params_1\" does not exist as a directory/file or is not accessible!">/dev/stderr
error_encountered="true"
fi
printf "\n">/dev/tty
error1="false"
error2="false"
[ -e "$params_2" ] && {
if [ -d "$params_2" ]; then
cd "$params_2" >/dev/null 2>/dev/null && {
dir2="$PWD"
cd "$initial_dir"
}||{
error1="true"
}
elif [ ! -d "$params_2" ]; then
file2="$params_2"
fi
}||{
error2="true"
}
if [ "$error1" = "true" -o "$error2" = "true" ]; then
printf '%s\n' "ERROR: PARAMETER2: \"$params_2\" does not exist as a directory/file or is not accessible!">/dev/stderr
error_encountered="true"
fi
if [ "$error_encountered" = "true" ]; then
printf "\n">/dev/stderr
exit
fi
## TYPE ///// PATH ///// SIZE ///// LAST TIME WRITE IN SECONDS ##
if [ "$OS_CASE" = "1" ]; then #Linux OS
if [ -n "$dir1" ]; then
command1='find . -not -type d -exec stat -c "< ///// %n ///// %s ///// %Y" {} \;'
else
command1_string="$(stat -c "< ///// %n ///// %s ///// %Y" "$file1")"
command1="printf '%s\n' \"\$command1_string\""
fi
if [ -n "$dir2" ]; then
command2='find . -not -type d -exec stat -c "> ///// %n ///// %s ///// %Y" {} \;'
else
command2_string="$(stat -c "> ///// %n ///// %s ///// %Y" "$file2")"
command2="printf '%s\n' \"\$command2_string\""
fi
command3='date -d @'
cd "$initial_dir"
sort_command="sort -k 3"
uniq_command="uniq -u -f 2"
elif [ "$OS_CASE" = "2" ]; then #Mac OS
if [ -n "$dir1" ]; then
command1='find . -not -type d -exec stat -f "< ///// %N ///// %z ///// %m" {} \;'
else
command1_string="$(stat -f "< ///// %N ///// %z ///// %m" "$file1")"
command1="printf '%s\n' \"\$command1_string\""
fi
if [ -n "$dir2" ]; then
command2='find . -not -type d -exec stat -f "> ///// %N ///// %z ///// %m" {} \;'
else
command2_string="$(stat -f "> ///// %N ///// %z ///// %m" "$file2")"
command2="printf '%s\n' \"\$command2_string\""
fi
command3='date -j -f %s '
cd "$initial_dir"
sort_command="sort -k 3"
uniq_command="uniq -u -f 2"
else
printf '\n%s\n\n' "Error: Unsupported OS!">/dev/stderr
exit 1
fi
#Trap "INTERRUPT" (CTRL-C) and "TERMINAL STOP" (CTRL-Z) signals:
trap 'trap1' INT
trap 'trap1' TSTP
old_IFS="$IFS" #Store initial IFS value
IFS="
"
set -f #Set shell flags (disable globbing):
found_previous="false"
count=0
skip=0
if [ -n "$dir1" -o -n "$dir2" ]; then
for line in $(\
if [ -n "$dir1" -a -n "$dir2" ]; then\
Proc1;\
elif [ -z "$dir1" -o -z "$dir2" ]; then\
Proc2;\
fi;\
); do
count=$((count+1))
PrintJustInTitle "Analyzing file $count..."
if [ -z "$current_line_file_type" ]; then
current_line="$line"
current_line_file_type="${line%%" ///// "*}"
current_line_file_mtime_in_seconds="${line##*" ///// "}"
current_line_file_type_path_and_size="${line%" ///// "*}"
current_line_file_size="${current_line_file_type_path_and_size##*" ///// "}"
current_line_file_type_path="${line%" ///// "*" ///// "*}"
current_line_file_path="${current_line_file_type_path#*" ///// "}"
else
previous_line="$current_line"
previous_line_file_type="$current_line_file_type"
previous_line_file_mtime_in_seconds="$current_line_file_mtime_in_seconds"
previous_line_file_type_path_and_size="$current_line_file_type_path_and_size"
previous_line_file_size="$current_line_file_size"
previous_line_file_type_path="$current_line_file_size"
previous_line_file_path="$current_line_file_path"
current_line="$line"
current_line_file_type="${line%%" ///// "*}"
current_line_file_mtime_in_seconds="${line##*" ///// "}"
current_line_file_type_path_and_size="${line%" ///// "*}"
current_line_file_size="${current_line_file_type_path_and_size##*" ///// "}"
current_line_file_type_path="${line%" ///// "*" ///// "*}"
current_line_file_path="${current_line_file_type_path#*" ///// "}"
if [ ! "$skip" = "$count" ]; then
if [ "$found_previous" = "false" ]; then
seconds_difference=$(($current_line_file_mtime_in_seconds - $previous_line_file_mtime_in_seconds))
if [ \
\( "$current_line" = "$previous_line" \) -o \
\( \
\( "$current_line_file_path" = "$previous_line_file_path" \) -a \
\( "$current_line_file_size" = "$previous_line_file_size" \) -a \
\( "$seconds_difference" = "1" -o "$seconds_difference" = "-1" \) \
\) \
]; then
found_previous="true"
skip=$((count+1))
else
printf '%s\n' "$previous_line_file_type $previous_line_file_path - ""Size: ""$previous_line_file_size"" Bytes"" - ""Modified Date: ""$(eval $command3$previous_line_file_mtime_in_seconds)"
found_previous="false"
fi
else
printf '%s\n' "$previous_line_file_type $previous_line_file_path - ""Size: ""$previous_line_file_size"" Bytes"" - ""Modified Date: ""$(eval $command3$previous_line_file_mtime_in_seconds)"
found_previous="false"
fi
else
found_previous="false"
fi
fi
done
#Treat last case separately:
if [ "$count" -gt "0" ]; then
if [ "$found_previous" = "false" ]; then
printf '%s\n' "$current_line_file_type $current_line_file_path - ""Size: ""$current_line_file_size"" Bytes"" - ""Modified Date: ""$(eval $command3$current_line_file_mtime_in_seconds)"
fi
fi
else
line1=""
line2=""
for line in $(\
if [ -n "$dir1" -a -n "$dir2" ]; then\
Proc1;\
elif [ -z "$dir1" -o -z "$dir2" ]; then\
Proc2;\
fi;\
); do
if [ -z "$line1" ]; then
line1="$line"
line1_file_type="${line%%" ///// "*}"
line1_file_mtime_in_seconds="${line##*" ///// "}"
line1_file_type_path_and_size="${line%" ///// "*}"
line1_file_size="${line1_file_type_path_and_size##*" ///// "}"
line1_file_type_path="${line%" ///// "*" ///// "*}"
line1_file_path="${line1_file_type_path#*" ///// "}"
else
line2="$line"
line2_file_type="${line%%" ///// "*}"
line2_file_mtime_in_seconds="${line##*" ///// "}"
line2_file_type_path_and_size="${line%" ///// "*}"
line2_file_size="${line2_file_type_path_and_size##*" ///// "}"
line2_file_type_path="${line%" ///// "*" ///// "*}"
line2_file_path="${line2_file_type_path#*" ///// "}"
seconds_difference=$(($line2_file_mtime_in_seconds - $line1_file_mtime_in_seconds))
if [ \
\( "$line2_file_size" = "$line1_file_size" \) -a \
\( \
\( "$line2_file_mtime_in_seconds" = "$line1_file_mtime_in_seconds" \) -o \
\( "$seconds_difference" = "1" -o "$seconds_difference" = "-1" \) \
\) \
]; then
:;
else
printf '%s\n' "$line1_file_type $line1_file_path - ""Size: ""$line1_file_size"" Bytes"" - ""Modified Date: ""$(eval $command3$line1_file_mtime_in_seconds)"
printf '%s\n' "$line2_file_type $line2_file_path - ""Size: ""$line2_file_size"" Bytes"" - ""Modified Date: ""$(eval $command3$line2_file_mtime_in_seconds)"
fi
fi
done
fi
CleanUp
fi
- 它能做什么:
- 通过以下方式比较(递归地)作为参数提供的两个目录树路径中的文件(我们将它们表示为:
<dir_tree1>
和<dir_tree2>
):- 相对路径
- 尺寸
- 修改日期
- 如果发现差异:它会列出不同文件的相对路径及其详细信息(大小和修改日期):
- 文件的相对文件路径以 ' '
<dir_tree1>
为前缀<
- 文件的相对文件路径以 ' '
<dir_tree2>
为前缀>
- 文件的相对文件路径以 ' '
- 笔记:
- 仅比较文件,不比较目录
- 应该可以在 Linux 操作系统、Mac 操作系统上运行 - 无需安装任何其他工具
答案3
不diff
- 你不需要为此查看文件 - 只需将该信息与stat
shell 中的信息进行比较,如
if [[ $(stat -c%s_%Y file1) == $(stat -c%s_%Y file2) ]]
then echo equal
else echo different
fi
该stat
命令提供来自文件索引节点的信息,-c
允许您选择所需的属性(%s
根据%Y
您的情况)。