【问题标题】:Find all duplicate subdirectories in directory查找目录中所有重复的子目录
【发布时间】:2016-10-02 14:30:33
【问题描述】:

我需要制作一个“列出当前工作目录下所有相同的子目录(递归)”的 shell 脚本。

我是 shell 脚本的新手。我该如何处理?

对我来说,这意味着:

  • 对于从某个起始目录开始的每个目录,将其与按名称共享的每个其他目录进行比较。
    • 如果其他目录具有相同的名称,请检查大小。
      • 如果大小也相同,递归地逐项比较每个目录的内容,可能是md5sum(?) 并继续对目录中的每个子目录进行比较(递归地?)
  • 然后,通过在遇到的每个子目录上递归调用它来继续
  • 然后,对目录结构中的每个其他目录重复此操作

这将是我写过的最复杂的程序,所以我想我只是不知道有一些 shell 命令可以为我完成大部分工作?

即,我应该如何处理这个问题?所有其他部分都是关于谷歌搜索,直到我发现 shell 命令为我完成了 90% 的工作。

(对于之前未能完成的作业,这部分得了零分,需要知道以后如何处理。)

【问题讨论】:

  • 输入输出样例?
  • 没有给出,我假设输入是当前工作目录,输出是被认为相同的目录列表。抱歉,不能更具体
  • 取决于您对重复的具体含义。 dir1/file{1,2,3}dir2/file{1.2,3} 其中file{1,2,3} 与重复计数相同吗?注意 dir1/dir2 是不同的。如果dir2/file{1,2,3} 下降了 3 个级别,这是否算作与原始dir1 中相同文件的匹配?这里有很多可能性,学习机会可能在于“您如何确定需要被视为欺骗(以及为什么)的正确规范?”。祝你好运。

标签: bash shell unix recursion


【解决方案1】:

如果听说有一个特殊的 Unix 工具或标准 Unix 工具的特殊用法可以完全按照您的描述进行操作,我会感到惊讶。也许您对任务的理解比任务提供者的意图更复杂。也许与“相同”有关的东西是关于链接的。通常情况下,硬链接目录是不允许的,所以这可能也不是这个意思。

无论如何,我会通过为树中的所有节点创建校验和来完成此任务,即。 e.递归:

  • 对于目录,获取所有条目的名称及其校验和(递归)并计算它们的校验和,
  • 对于纯文件计算其内容的校验和,
  • 对于符号链接和特殊文件(设备等),请考虑您想要什么(我将忽略它)。

为所有元素创建校验和后,搜索重复项(通过对所有元素的列表进行排序并搜索连续行)。

一个快速的解决方案可能是这样的:

#!/bin/bash

dirchecksum() {
  if [ -f "$1" ]
  then
    checksum=$(md5sum < "$1")
  elif [ -d "$1" ]
  then
    checksum=$(
      find "$1" -maxdepth 1 -printf "%P " \( ! -path "$1" \) \
                -exec bash -c "dirchecksum {}" \; |
        md5sum
    )
  fi
  echo "$checksum"
  echo "$checksum $1" 1>&3
}
export -f dirchecksum

list=$(dirchecksum "$1" 3>&1 1>/dev/null)

lastChecksum=''
while read checksum _ path
do
  if [ "$checksum" = "$lastChecksum" ]
  then
    echo "duplicate found: $path = $lastPath"
  fi
  lastChecksum=$checksum
  lastPath=$path
done < <(sort <<< "$list")

这个脚本使用了两个可能不太清楚的技巧,所以我提到它们:

  • 要将一个shell函数传递给find -exec,可以export -f它(在它下面完成)然后调用bash -c ...来执行它。
  • shell 函数有 两个 输出流,一个用于返回结果校验和(这是通过标准输出,即 fd 1),一个用于给出在到达此的路上找到的每个校验和(这个是通过 fd 3)。

最后的排序使用 fd 3 给出的列表作为输入。

【讨论】:

    【解决方案2】:

    可能是这样的:

    $ find -type d -exec sh -c "echo -n {}\  ; sh -c \"ls -s {}; basename {}\"|md5sum " \; | awk '$2 in a {print "Match:"; print a[$2], $1; next} a[$2]=$1{next}'
    Match:
    ./bar/foo ./foo
    

    find所有目录:find -type d,输出:

    .
    ./bar
    ./bar/foo
    ./foo
    

    ls -s {}; basename {} 将打印简化的目录列表和列出的目录的基本名称,例如目录fools -s foo; basename foo

    total 0
    0 test
    foo
    

    这些将涵盖每个目录中的文件、它们的大小和目录名称。该输出将被发送到md5sum 并沿目录发送:

    . 674e2573b49826d4e32dfe81d9680369  -
    ./bar 4c2d588c5fa9781ad63ad8e86e575e01  -
    ./bar/foo ff8d1569685be86366f18ea89851db35  -
    ./foo ff8d1569685be86366f18ea89851db35  -
    

    将发送至awk:

    $2 in a {            # hash as array key
        print "Match:"   # separate hits in output
        print a[$2], $1  # print matching dirscompared to
        next             # next record
    } 
    a[$2]=$1 {next}      # only first match is stored and 
    

    测试目录结构:

    $ mkdir -p test/foo; mkdir -p test/bar/foo; touch test/foo/test; touch test/bar/foo/test
    $ find test/
    test/
    test/bar
    test/bar/foo
    test/bar/foo/test  # touch test
    test/foo
    test/foo/test      # touch test
    

    【讨论】:

      猜你喜欢
      • 2018-02-12
      • 1970-01-01
      • 2020-05-31
      • 1970-01-01
      • 2013-03-20
      • 1970-01-01
      • 2018-01-13
      • 2017-05-01
      • 2020-08-29
      相关资源
      最近更新 更多