如何递归地比较两个目录并检查其中一个目录是否包含另一个目录?

我有两个目录,它们包含公共文件。 我想知道一个目录是否包含与另一个目录相同的文件。 我在网上找到了一个脚本但是我想要提升它来递归。

#!/bin/bash # cmp_dir - program to compare two directories # Check for required arguments if [ $# -ne 2 ]; then echo "usage: $0 directory_1 directory_2" 1>&2 exit 1 fi # Make sure both arguments are directories if [ ! -d $1 ]; then echo "$1 is not a directory!" 1>&2 exit 1 fi if [ ! -d $2 ]; then echo "$2 is not a directory!" 1>&2 exit 1 fi # Process each file in directory_1, comparing it to directory_2 missing=0 for filename in $1/*; do fn=$(basename "$filename") if [ -f "$filename" ]; then if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi fi done echo "$missing files missing" 

有人会建议一个算法吗?

 #!/bin/bash # cmp_dir - program to compare two directories # Check for required arguments if [ $# -ne 2 ]; then echo "usage: $0 directory_1 directory_2" 1>&2 exit 1 fi # Make sure both arguments are directories if [ ! -d "$1" ]; then echo "$1 is not a directory!" 1>&2 exit 1 fi if [ ! -d "$2" ]; then echo "$2 is not a directory!" 1>&2 exit 1 fi # Process each file in directory_1, comparing it to directory_2 missing=0 while IFS= read -r -d $'\0' filename do fn=${filename#$1} if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0) echo "$missing files missing" 

请注意,我在上面的各个地方添加了大约$1 $2$2 $1双引号,以保护它们的shell扩展。 如果没有双引号,带空格或其他困难字符的目录名将导致错误。

关键循环现在是:

 while IFS= read -r -d $'\0' filename do fn=${filename#$1} if [ ! -f "$2/$fn" ]; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0) 

这使用find以递归方式跳转到目录$1并查找文件名。 构造while IFS= read -r -d $'\0' filename; do .... done < <(find "$1" -type f -print0) while IFS= read -r -d $'\0' filename; do .... done < <(find "$1" -type f -print0)对所有文件名都是安全的。

不再使用basename因为我们正在查看子目录中的文件,我们需要保留子目录。 因此,代替对basename的调用,使用了行fn=${filename#$1} 。 这只是从filename删除包含目录$1的前缀。

问题2

假设我们按名称匹配文件,但不管目录如何。 换句话说,如果第一个目录包含文件a/b/c/some.txt ,如果文件some.txt存在于第二个目录的任何子目录中,我们将认为它存在于第二个目录中。 为此,请将上面的循环替换为:

 while IFS= read -r -d $'\0' filename do fn=$(basename "$filename") if ! find "$2" -name "$fn" | grep -q . ; then echo "$fn is missing from $2" missing=$((missing + 1)) fi done < <(find "$1" -type f -print0) 

FSlint是一个小型GUI应用程序,可帮助您识别和清理冗余文件系统。

安装FSlint

从Ubuntu软件中心或命令行安装FSlint,如下所示:

 sudo apt-get install fslint 

(在我的系统上,安装FSlint没有引入额外的依赖。具体来说,fslint依赖于findutils,python和python-glade2,它们都应该在你的系统上。你可以使用软件中心删除FSlint或输入sudo apt-get autoremove --purge fslint在终端中sudo apt-get autoremove --purge fslint )。

搜索文件

从Unity Dash启动FSlint。

这是主屏幕的屏幕截图。 有许多高级function,但应用程序的基本用法相对简单。

单击左上角的“ Add按钮以添加要检查的所有目录。 显然,您可以使用“ Remove按钮删除目录。

在此处输入图像描述

确保recurse? 选中右侧的复选框。 然后单击“ Find按钮。 (任何错误,例如文件权限问题,都将打印在FSlint窗口的底部)。

FSlint将列出所有重复文件,其目录位置和文件日期。 FSlint还会向您显示由于冗余文件而浪费的字节数。

删除重复项

现在,您可以使用ShiftCtrl键和鼠标左键选择多个文件。 如果要自动选择多个文件,请单击“ Select按钮,您将获得选项,例如根据日期选择文件或输入通配符选择条件。

如果您需要使用FSlint之外的所选文件列表(可能作为您自己脚本的输入),请单击“ Save按钮以保存文本文件。

最后,您可以使用“ Delete按钮删除所选文件,也可以使用“ Merge按钮合并所选文件。 请注意,“ Mergefunction会从系统中删除未选择的文件,并创建指向相应所选文件的硬链接。 如果您想保留现有的文件结构,但希望释放系统上的一些空间,则可以使用此function。

在此处输入图像描述

其他function和文档

FSlint具有其他强大function,可从左窗格中的选项卡访问。 我发现Name clashes在有相同名称但不同的文件时很有用(可能是因为你在另一个目录中保存了较新版本的文件)。

FSlint窗口顶部还有一个Advanced search parameters选项卡,允许您在搜索中排除某些目录,或使用参数过滤结果。

这个简单的小工具有很多强大的function。 它可以节省您编写和调试脚本的工作量。 您可以在http://www.pixelbeat.org/fslint/上找到更多信息 。 以下是英文指南的直接链接: http : //en.flossmanuals.net/fslint/ 。

我想分享这种方式,因为我认为这很有趣。

对于目标文件夹下的每个子文件夹,我们为该子文件夹生成一个哈希。

每个文件夹哈希都是从散列该文件夹下的所有文件的结果生成的。 因此,在同一结构中包含相同文件的任何文件夹都应生成相同的哈希!

最后,我们使用uniq仅显示重复的文件夹哈希值。

将以下脚本保存为seek_duplicate_folders.sh ,然后像这样运行:

 $ bash seek_duplicate_folders.sh [root_folder_to_scan] 

这是脚本:

 #!/bin/bash target="$1" hash_folder() { echo "Hashing $1" >/dev/stderr pushd "$1" >/dev/null # Hash all the files find . -type f | sort | xargs md5sum | # Hash that list of hashes, discard the newline character, # and append the folder name md5sum - | tr -d '\n' printf " %s\n" "$1" popd >/dev/null } find "$target" -type d | while read dir do hash_folder "$dir" done | sort | # Display only the lines with duplicate hashes (first 32 chars are duplicates) uniq -D -w 32 

注意事项:

  • 效率低下:它多次在树的深处md5sums文件(每个祖先文件夹一次)
  • 不检测文件时间戳,所有权或权限的差异
  • 忽略空文件夹。 因此,两个包含相同文件或没有文件但在其中包含不同空文件夹的文件夹仍将报告为相同。
  • 忽略符号链接。 包含不同符号链接的文件仍可能报告为相同。