[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[系统相关] 批处理如何查找完全相同的文件夹?

一个或几个文件夹中有很多子文件夹,其中有些子文件夹里面的内容完全一模一样,如何把这些完全相同的子文件夹(注意:不是文件)从众多的文件夹中找出来,便于删除?


请不吝指教 。谢谢。

http://bbs.bathome.net/viewthrea ... C8%C8%FC&page=1

参考第九题 以及后面其他论坛会员的方案

TOP

本帖最后由 taofan712 于 2017-1-17 15:44 编辑

小菜鸟借楼继续提问,请问下我的代码为什么在处理tmp.txt的时候,没能把重复的行找出来 ?
我想的是把楼主问题里的文件夹的几个特征(文件个数,对象个数,字节大小)写进tmp.txt,然后对比字节大小,相同的就写进结果文本。再根据文件个数等特征肉眼判断文件夹是否一模一样。。。。。
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%a in ('dir /ad /b /s f:\test') do (
  4. for /f %%b in ('dir /a /b %%~fa^|find /v /c ""') do set  n=%%b
  5. for /f "delims=" %%c in ('dir %%~fa^|find "个文件"') do set m=%%c
  6. set str=!n!_!m:~0,-3!
  7. echo %%~fa  !str!>>tmp.txt
  8. )
  9. for /f "tokens=1,2,3,4,5 delims= " %%i in (tmp.txt) do (
  10. echo %%i %%j %%k %%l %%m & pause>nul
  11. if not defined %%m (set %%m=D
  12. ) else (find "%%m" tmp.txt>>rst.txt & echo _____>>rst.txt)
  13. )
复制代码

TOP

本帖最后由 523066680 于 2017-1-17 22:40 编辑

回复 3# taofan712

    以前考虑过,但是过了太久,忘了遇到什么问题了。

回想一下吧:

  • 可能会遇到目录自身的链接而陷入无限递归的情况(symbolic link),然后 dir /s 就输出大量冗余的结果并提示 “文件名太长”。
    虽然 dir 会告诉你这是个 <JUNCTION>
    举个例子
    1. set name=abcdefghijklmnopqrstuvwxyz
    2. mkdir %name%
    3. mklink /J .\%name%\%name% .\%name%
    复制代码
  • 可能会遇到非一般GBK字符的文件名,比如  "董贞 ・ 01.剑如虹.[贞江湖].mp3",需要确保能够正确访问这些文件的信息。

  • 如果使用文件短名,也可能出现冲突的情况

  • 当对比到两个目录内的文件一致的时候,它们的上一级目录也可能是完全一致的,甚至上上层,也意味着会有很多相同的子目录被冗余地显示出来。


准备用 C 配合 Perl 实现判断相同目录的程序,要做的事情很多,看心情吧

TOP

回复 3# taofan712


    两个字节数相同的文件,它们的内容可能不同,根据这种判断来删除文件夹是不是风险太大啦,没啥实用价值吧。

TOP

本帖最后由 523066680 于 2017-1-18 14:50 编辑

回复 5# GNU

      列出疑似相同的目录然后可以做进一步的深层的对比,然后将结果列举作为参考,并非即刻删除。
至于实用价值,你不需要,不代表别人用不上。

举例一个场景,某职位频繁更换职员,新上任的职员一般会对之前的资料复制副本保存在某个位置(最外层目录名可能被修改)。
但是有一任职员突然辞职,未能完成交接。而电脑上的资料又非常重要,目录体系非常复杂,且占用磁盘。
刚上任的职员如果在不了解文件名(但是懂程序)的情况下,如何找到分布在磁盘的冗余的目录副本?

关于判断文件是否相同,用 MD5 校验值即可保证准确度(文件越大越耗时)。

TOP

回复 6# 523066680


    我很赞同,希望对楼主有用。

TOP

回复 5# GNU


    先找到字节一样大小的,然后根据其他特征判断啊,比如文件数量,子文件夹数量,这两个特征也相同的话,应该是90%以上的可能是文件夹重复了。

TOP

回复 8# taofan712


    同意。希望楼主不在乎剩下10%的误删概率。

TOP

本帖最后由 523066680 于 2017-1-18 20:34 编辑

8楼人家说的概率是草率了点,可是人家没说删除呀。

希望楼主,希望楼主,楼主的心都让你操碎了。

想起之前看的一篇文章:有用,无用

TOP

本帖最后由 523066680 于 2017-1-18 21:21 编辑

初步达到清除冗余信息的要求,
对以E盘备份资料处理了一次,贴部分结果,代码有待完善。

处理方法:
将目录树的信息整个重组为 Perl 的哈希(键值对)数据结构。将每个目录下的结构信息dump出来,作为字符串,
使用 md5 函数转成 MD5校验值 作为标记,减少大量字符串对比的开销、内存开销。

信息去重:
获得第一阶段的疑似重复目录信息后,有很多是多余的结果,
清理方法:如果某一组目录是另一组目录的子目录,进行排除。(比较折腾就是了)

优化考虑:
通过文件累计数量 / 文件累计大小作为阀值,只输出足够复杂且内容相同的目录树(那些微不足道的暂时pass或另外输出)。
  1. E:\售后\以色列升级工具\USB Debug Tool\USB_debug_tool_driver\tool\MProg 2.3
  2. E:\S烧录程序_软件\升级工具MST712齐总\USB_debug_tool_driver\tool\MProg 2.3
  3. E:\售后\DVD升级工具 for 印尼谢总\USB Debug Tool\USB_debug_tool_driver\tool\MProg 2.3
  4. {
  5.   "Drivers"         => {
  6.                          "D2XX Release Info.txt" => {},
  7.                          "FT8U2XX.RES"           => {},
  8.                          "Ftccomms.vxd"          => {},
  9.                          "FTCSENUM.SYS"          => {},
  10.                          "FTCSENUM.VXD"          => {},
  11.                          "FTCSER.INF"            => {},
  12.                          "FTCSER2K.SYS"          => {},
  13.                          "ftcser98.sys"          => {},
  14.                          "FTCSMOU.INF"           => {},
  15.                          "FTCSMOU.VXD"           => {},
  16.                          "FTCSUI.DLL"            => {},
  17.                          "FTCSUI2.DLL"           => {},
  18.                          "ftcun2k.ini"           => {},
  19.                          "ftcun98.ini"           => {},
  20.                          "ftcunin.exe"           => {},
  21.                          "FTCUSB.INF"            => {},
  22.                          "FTCUSB.SYS"            => {},
  23.                          "FTD2XX.DLL"            => {},
  24.                          "Ftd2xx.h"              => {},
  25.                          "FTD2XX.INF"            => {},
  26.                          "FTD2XX.LIB"            => {},
  27.                          "FTD2XX.SYS"            => {},
  28.                          "Ftd2xxpg.rtf"          => {},
  29.                          "FTD2XXUN.EXE"          => {},
  30.                          "FTD2XXUN.INI"          => {},
  31.                          "PG_Extra.doc"          => {},
  32.                          "ReleaseNotes.doc"      => {},
  33.                        },
  34.   "FTD2XX.DLL"      => {},
  35.   "FTUninstall.bat" => {},
  36.   "Help"            => { "MProg.chm" => {} },
  37.   "license.txt"     => {},
  38.   "MProg.exe"       => {},
  39.   "Templates"       => {
  40.                          "com2p.ept"    => {},
  41.                          "default.ept"  => {},
  42.                          "default1.ept" => {},
  43.                          "MSTAR.ept"    => {},
  44.                        },
  45.   "uninstall.exe"   => {},
  46.   "uninstall.ini"   => {},
  47. }
复制代码

TOP

这个问题用批处理来做本身就不合理,代码量大大超出了使用价值.用其他更好的语言来写就能简单完成.
要考虑的不单单是完全相同那么简单,如果文件夹里面的文件是几个G你确定你还能继续用bat来解决此问题?

TOP

目录树结构转 Perl 哈希结构

本帖最后由 523066680 于 2017-1-18 21:39 编辑
  1. =info
  2.     Code by 523066680@163.com
  3. =cut
  4. # 备注,可能遇到 Unicode 文件名路径,
  5. # 暂时使用系统 cmd /U /c dir 生成路径列表
  6. use Encode;
  7. use IO::Handle;
  8. use Data::Dump qw/dump dd/;
  9. our $hash = {};
  10. my $gstr;
  11. {
  12.     local $/ = "\x0d\x00\x0a\x00";
  13.     print "getting file lists ...\n";
  14.     @files = `cmd /U /c dir /s /b C:\\MinGW`;
  15.     print "Deal ...\n";
  16.     for my $n ( 0 .. $#files )
  17.     {
  18.         $files[$n] =~s/\x0d\x00\x0a\x00//;
  19.         $gstr = encode('gbk', decode('utf16-le', $files[$n]));
  20.         toStruct( $gstr );
  21.     }
  22. }
  23. #举个栗子
  24. dd( $hash->{'C:'}{'MinGW'}{'include'}{'boost'}{'accumulators'} );
  25. sub toStruct
  26. {
  27.     my $path = shift;
  28.     my @parts = split(/[\/\\]/, $path);
  29.     my $ref;
  30.     $ref = $hash;
  31.     grep
  32.     {
  33.         $ref->{$_} = {} unless ( exists $ref->{$_} );
  34.         $ref = $ref->{$_};
  35.     }
  36.     @parts;
  37. }
复制代码
部分 Dump 结果
  1. getting file lists ...
  2. Deal ...
  3. {
  4.   "accumulators.hpp"     => {},
  5.   "accumulators_fwd.hpp" => {},
  6.   "framework"            => {
  7.                               "accumulator_base.hpp"    => {},
  8.                               "accumulator_concept.hpp" => {},
  9.                               "accumulator_set.hpp"     => {},
  10.                               "accumulators"            => {
  11.                                                              "droppable_accumulator.hpp" => {},
  12.                                                              "external_accumulator.hpp"  => {},
  13.                                                              "reference_accumulator.hpp" => {},
  14.                                                              "value_accumulator.hpp"     => {},
  15.                                                            },
  16.                               "depends_on.hpp"          => {},
  17.                               "external.hpp"            => {},
  18.                               "extractor.hpp"           => {},
  19.                               "features.hpp"            => {},
  20.                               "parameters"              => {
  21.                                                              "accumulator.hpp" => {},
  22.                                                              "sample.hpp"      => {},
  23.                                                              "weight.hpp"      => {},
  24.                                                              "weights.hpp"     => {},
  25.                                                            },
  26.                             },
  27.   "numeric"              => {
  28.                               "detail" => {
  29.                                 "function1.hpp"     => {},
  30.                                 "function2.hpp"     => {},
  31.                                 "function3.hpp"     => {},
  32.                                 "function4.hpp"     => {},
  33.                                 "function_n.hpp"    => {},
  34.                                 "pod_singleton.hpp" => {},
  35.                               },
  36.                               "functional" => { "complex.hpp" => {}, "valarray.hpp" => {}, "vector.hpp" => {} },
  37.                               "functional.hpp" => {},
  38.                               "functional_fwd.hpp" => {},
  39.                             },
复制代码
对于文件/目录辨别、符号链接(symbolic link)判断、文件大小等属性的提取,Perl 也不太合适,
还是 C 实在,从根本解决问题,已经写了一部分功能

TOP

回复 10# 523066680


    别瞎说,我没有操楼主的心。不信你问问楼主。我无所谓。你胡乱玷污楼主滴清白,小心楼主告你造谣。

TOP

本帖最后由 523066680 于 2017-1-19 17:21 编辑

备注:

  • 使用过程将在 D 盘生成 dirs.txt 和 md5map.txt,dirs.txt 为dir输出结果(Unicode),
    md5map为所有目录的校验值
  • 使用cmd /U /c 执行 dir 命令以确保Unicode字符的完整输出
  • 使用 YAML::Tiny Dump 替代 Data:ump,减少结构体输出的体积
  • 内存占用情况:如果 dir 导出的文件大小为 100MB,则内存占用约为 250MB
  • 冗余信息的处理:
    假设某个目录存在副本,且子目录下也存在副本
    1. ├─Fold
    2. │  ├─a
    3. │  └─a - 副本
    4. └─Fold - 副本
    5.     ├─a
    6.     └─a - 副本
    复制代码
    程序将只显示  fold 这一层的结果。但是,如果在副本的中间某一层存在差异,则会详尽地列出相同的子目录

  • 效率:个人PC(双核4G旧主机蓝标硬盘),dir /a /s /b D: ,254506 行数据, 从 dir 到 Perl 输出判断结果
    耗时 58 秒
  • 该 Perl 代码可读性:自带混淆
  • 未完待续
  1. =info
  2.     Code by 523066680@163.com
  3.     2017-01
  4. =cut
  5. use Encode;
  6. use IO::Handle;
  7. use Digest::MD5 qw/md5_hex/;
  8. use YAML::Tiny;
  9. STDOUT->autoflush(1);
  10. print "Dir ...\n";
  11. #system("cmd /U /c dir /a /s /b C:\\ >D:\\dirs.txt");
  12. print "Path to struct ...\n";
  13. our $hash = {};
  14. our @lines;
  15. our %fold_md5;
  16. our %md5map;
  17. my $gstr;
  18. open READ,"<:raw",'D:\\dirs.txt';
  19. {
  20.     local $/ = "\x0d\x00\x0a\x00";
  21.     while ($line = <READ>)
  22.     {
  23.         $line =~s/\x0d\x00\x0a\x00//;
  24.         $gstr = encode('gbk', decode('utf16-le', $line));
  25.         push @lines, $gstr;
  26.         toStruct( $gstr );
  27.     }
  28. }
  29. close READ;
  30. print "Compare ...\n";
  31. compare();
  32. sub compare
  33. {
  34.     my @parts;
  35.     my $ref;
  36.     my $md5;
  37.     print "Getting md5 information ...\n";
  38.     open WRT, ">:raw", "D:\\md5map.txt";
  39.     for my $i ( 0 .. $#lines )
  40.     {
  41.         @parts = split(/[\/\\]/, $lines[$i]);
  42.         $ref = $hash;
  43.         grep { $ref = $ref->{$_} } ( @parts );   #将引用迭代到路径的最后一层
  44.         next if (! keys %{$ref} );               #如果没有下一层文件内容则略过
  45.         
  46.         $md5 = md5_hex( Dump($ref) );
  47.         $fold_md5{ $lines[$i] } = $md5;
  48.         print WRT $md5 ." ". encode('utf8', decode('gbk', $lines[$i])) ."\n";
  49.     }
  50.     close WRT;
  51.     for my $k ( keys %fold_md5 )
  52.     {
  53.         $md5 = $fold_md5{$k};
  54.         push @{ $md5map{$md5} }, $k;
  55.     }
  56.     #去重,如果一组MD5内容相同的目录,且它们上一级目录的MD5也相同
  57.     #则无需列出。
  58.     print "Cut repeat case ...\n";
  59.     my $tp;
  60.     my $prev;
  61.     for my $m ( keys %md5map )
  62.     {
  63.         if ( $#{$md5map{$m}} > 0  )
  64.         {
  65.             undef $md5;
  66. ST:         for my $p ( @{ $md5map{$m} } )
  67.             {
  68.                 #取得上一层路径
  69.                 $tp = $p;
  70.                 $tp =~ s/(\\|\/)[^\\\/]+$//;
  71.                 if (    ( defined $md5 )
  72.                     and ( $md5 eq $fold_md5{$tp} ) #MD5相同
  73.                     and ( $prev ne $tp )           #并且不是相邻目录
  74.                     #例如 "Fold\a" "Fold\a副本" 的上一级都是 Fold,md5一致
  75.                 )
  76.                 {
  77.                     delete $md5map{$m};
  78.                     last ST;
  79.                 }
  80.                 else
  81.                 {
  82.                     $md5 = $fold_md5{$tp};
  83.                 }
  84.                 $prev = $tp;
  85.             }
  86.         }
  87.         else
  88.         {
  89.             #删除没必要列出的情况
  90.             delete $md5map{$m};
  91.         }
  92.     }
  93.     print "Find same folder\n";
  94.     for my $k ( keys %md5map )
  95.     {
  96.         print join("\n", @{$md5map{$k}} );
  97.         print "\n";
  98.         #dump_byPath( $md5map{$k}->[0] );
  99.         print "\n";
  100.     }
  101. }
  102. sub dump_byPath
  103. {
  104.     my $path = shift;
  105.     my $ref = $hash;
  106.     for my $e ( split(/[\/\\]/, $path) )
  107.     {
  108.         $ref = $ref->{$e};
  109.     }
  110.     print Dump( $ref );
  111. }
  112. sub toStruct
  113. {
  114.     my $path = shift;
  115.     my @parts = split(/[\/\\]/, $path);
  116.     my $ref;
  117.     $ref = $hash;
  118.     for my $e ( @parts )
  119.     {
  120.         if ( not exists $ref->{$e} ) #如果不加判断,会不断地替换,最后只有一个路径的结构
  121.         {
  122.             $ref->{$e} = {};
  123.         }
  124.         $ref = $ref->{$e};
  125.     }
  126. }
复制代码
1

评分人数

TOP

返回列表