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

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

我有两个目录,它们包含公共文件。我想知道一个目录是否包含与另一个目录相同的文件。我在网上找到了一个脚本,但我想改进它以递归方式执行。

  #!/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"

有人会为其建议一种算法吗?

答案1

#!/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,以保护它们的 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)对所有文件名都是安全的。

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)

答案2

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

安装 FSlint

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

sudo apt-get install fslint

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

搜索文件

从 Unity Dash 启动 FSlint。

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

点击Add左上角的按钮添加您想要检查的所有目录。当然,您也可以使用该Remove按钮删除目录。

在此处输入图片描述

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

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

删除重复项

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

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

最后,您可以使用 按钮删除所选文件Delete,或者使用Merge按钮合并所选文件。请注意,该Merge功能会删除未选定文件并创建到相应文件的硬链接已选择文件。如果您想保留现有文件结构,但又想释放系统上的部分空间,则可以使用此功能。

在此处输入图片描述

附加功能和文档

FSlint 还有其他强大的功能,可通过左侧窗格中的选项卡访问。我发现Name clashes当文件名称相同但不同(可能是因为您将文件的较新版本保存在不同的目录中)时,此功能非常有用。

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

这个简单的小工具有很多强大的功能。它可以节省您编写和调试脚本的精力。您可以在以下位置了解更多信息http://www.pixelbeat.org/fslint/。以下是英文指南的直接链接:http://en.flossmanuals.net/fslint/

答案3

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

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

每个文件夹哈希都是根据该文件夹下所有文件的哈希值生成的。因此,任何包含相同结构中相同文件的文件夹都应产生相同的哈希值!

最后,我们用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

注意事项:

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

相关内容