Board logo

标题: [系统相关] 批处理如何查找完全相同的文件夹? [打印本页]

作者: arter369    时间: 2017-1-15 15:46     标题: 批处理如何查找完全相同的文件夹?

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


请不吝指教 。谢谢。
作者: 523066680    时间: 2017-1-16 10:54

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

参考第九题 以及后面其他论坛会员的方案
作者: taofan712    时间: 2017-1-17 15:42

本帖最后由 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. )
复制代码

作者: 523066680    时间: 2017-1-17 21:19

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

回复 3# taofan712

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

回想一下吧:


准备用 C 配合 Perl 实现判断相同目录的程序,要做的事情很多,看心情吧
作者: GNU    时间: 2017-1-18 13:22

回复 3# taofan712


    两个字节数相同的文件,它们的内容可能不同,根据这种判断来删除文件夹是不是风险太大啦,没啥实用价值吧。
作者: 523066680    时间: 2017-1-18 14:34

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

回复 5# GNU

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

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

关于判断文件是否相同,用 MD5 校验值即可保证准确度(文件越大越耗时)。
作者: GNU    时间: 2017-1-18 14:59

回复 6# 523066680


    我很赞同,希望对楼主有用。
作者: taofan712    时间: 2017-1-18 15:42

回复 5# GNU


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

回复 8# taofan712


    同意。希望楼主不在乎剩下10%的误删概率。
作者: 523066680    时间: 2017-1-18 18:10

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

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

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

想起之前看的一篇文章:有用,无用
作者: 523066680    时间: 2017-1-18 21:10

本帖最后由 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. }
复制代码

作者: Nsqs    时间: 2017-1-18 21:35

这个问题用批处理来做本身就不合理,代码量大大超出了使用价值.用其他更好的语言来写就能简单完成.
要考虑的不单单是完全相同那么简单,如果文件夹里面的文件是几个G你确定你还能继续用bat来解决此问题?
作者: 523066680    时间: 2017-1-18 21:35     标题: 目录树结构转 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 实在,从根本解决问题,已经写了一部分功能
作者: GNU    时间: 2017-1-18 21:44

回复 10# 523066680


    别瞎说,我没有操楼主的心。不信你问问楼主。我无所谓。你胡乱玷污楼主滴清白,小心楼主告你造谣。
作者: 523066680    时间: 2017-1-19 16:52

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

备注:
  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. }
复制代码

作者: 523066680    时间: 2017-1-20 15:19     标题: 这个时侯需要C的助攻了

本帖最后由 523066680 于 2017-1-20 16:27 编辑

枚举目录内容,如果是文件在开头给出尺寸,如果是文件夹,在开头给出 DIR 或者 FOLD 的标记,遇到链接自动绕开
输出UNICODE编码的字符。

须使用 g++ 编译
  1. /*
  2.     Code by:523066680@163.com
  3.     2017-01
  4. */
  5. #include <stdio.h>
  6. #include <fcntl.h>
  7. #include <dirent.h>
  8. #include <wchar.h>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #include <string.h>
  12. #include <windows.h>
  13. #define NAME_MAX 10240
  14. void func(wchar_t path[]);
  15. void console_print(const wchar_t str[]);
  16. void CheckConsoleRedirect(void);
  17. DWORD written = 0;
  18. static bool g_bRedirect = false;
  19. int wmain(int argc, wchar_t *argv[] )
  20. {
  21.     wchar_t pth[NAME_MAX] = L"D:\\Extra\\";
  22.    
  23.     //首先判断是否为重定向
  24.     CheckConsoleRedirect();
  25.     if ( argc >= 2 )
  26.     {
  27.         if ( argv[1][wcslen(argv[1])-1] == '\\' )
  28.             swprintf(pth, L"%ls", argv[1] );
  29.         else
  30.             swprintf(pth, L"%ls\\", argv[1] );
  31.     }
  32.     func( pth );
  33.     return 0;
  34. }
  35. void func( wchar_t path[] )
  36. {
  37.     DWORD attr;
  38.     _WDIR * a = _wopendir(path);
  39.     _wdirent * dp;
  40.     _WDIR * aa;
  41.     struct stat stbuf;
  42.     wchar_t full_info[NAME_MAX] = L"";
  43.     WCHAR fullpath[NAME_MAX] = L"";
  44.     swprintf(full_info, L"FOLD: %ls\r\n", path );
  45.     console_print( full_info );
  46.     //打印目录内容
  47.     while ( dp = _wreaddir(a) )
  48.     {
  49.         //排除 当前文件夹 和 上一级 文件夹
  50.         if (
  51.                ( wcscmp(dp->d_name, L"." )  == 0 )
  52.             || ( wcscmp(dp->d_name, L".." ) == 0 )
  53.         )
  54.         { continue; }
  55.         swprintf(fullpath, L"%ls%ls", path, dp->d_name);
  56.         wstat(fullpath, &stbuf);
  57.         attr = GetFileAttributesW(fullpath);
  58.         //排除符号链接类型的文件和目录
  59.         if ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) != FILE_ATTRIBUTE_REPARSE_POINT )
  60.         {
  61.             if ( (attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY )
  62.             {
  63.                 swprintf(full_info, L"%16ls %ls\r\n", L"DIR", dp->d_name );
  64.                 console_print( full_info );
  65.             }
  66.             else
  67.             {
  68.                 swprintf(full_info, L"%16lld %ls\r\n", stbuf.st_size, dp->d_name );
  69.                 console_print( full_info );
  70.             }
  71.         }
  72.     }
  73.     _wclosedir(a);
  74.     //递归流程
  75.     a = _wopendir(path);
  76.     while ( dp = _wreaddir(a) )
  77.     {
  78.         //排除 当前文件夹 和 上一级 文件夹
  79.         if (
  80.                ( wcscmp(dp->d_name, L"." )  == 0 )
  81.             || ( wcscmp(dp->d_name, L".." ) == 0 )
  82.         )
  83.         {
  84.             continue;
  85.         }
  86.         swprintf(fullpath, L"%ls%ls", path, dp->d_name);
  87.         attr = GetFileAttributesW(fullpath);
  88.         //排除符号链接类型的文件和目录
  89.         if ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) != FILE_ATTRIBUTE_REPARSE_POINT )
  90.         {
  91.             if ( (attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY )
  92.             {
  93.                 swprintf(fullpath, L"%ls%ls\\", path, dp->d_name);
  94.                 func( fullpath );
  95.             }
  96.         }
  97.     }
  98.     _wclosedir(a);
  99. }
  100. void console_print(const wchar_t str[])
  101. {
  102.     if ( ! g_bRedirect )
  103.     {
  104.         WriteConsoleW(
  105.             GetStdHandle(STD_OUTPUT_HANDLE), str, wcslen(str)  , &written, NULL
  106.         );
  107.     }
  108.     else
  109.     {
  110.         WriteFile(
  111.             GetStdHandle(STD_OUTPUT_HANDLE), str, wcslen(str) * sizeof( str[0] ) , &written, NULL
  112.         );
  113.     }
  114. }
  115. void CheckConsoleRedirect(void)
  116. {
  117.     if (!GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &written))
  118.     {
  119.         g_bRedirect = true;
  120.         WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\xFF\xFE", 2, &written, 0);
  121.     }
  122. }
复制代码
输出的一部分示例:
  1. Fold: D:\Extra\
  2.        21253 065738_6eeL_1428332.jpg
  3.       362664 1440339f6bzggbp0x16gp3.jpg
  4.       398975 144034a5tty5g2s2azd3bg.jpg
  5.       229915 144037xhfh2jhzh1g1b2jf.jpg
  6.        40421 6cc028c79f3df8dc7a6e5322cd11728b461028c5.jpg
  7.      1853657 7cefdfa5jw1ez6iowueepg206l067qv5.gif
  8.          DIR Book
  9.     11714560 freetype-2.4.0.tar
  10.      2903989 ft27.zip
  11.          DIR graphics
复制代码
如此一来可以顺便写一个文件夹按大小排序的脚本。
作者: 523066680    时间: 2017-1-20 16:58

感谢 happy886rr 的加分。

刚刚测试这个 C 有点问题,在遇到大于 4G 的文件时输出的文件大小不正确(超过4字节的溢出)。
把 stat() 改为 _wstat64()
把 struct stat  改为 struct _stat64
以及 printf 中的 %ld 改为 %lld 即可
作者: 523066680    时间: 2017-1-20 17:32     标题: 基本完成我要的功能 - 按占用大小来枚举

现在可以设定一个阀值,比如值列出大小超过100MB 且内容疑似相同的文件夹

代码自带混淆,看官自觉绕道
  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 "Getting file list ...\n";
  11. system('listv1.3 D: >D:\a.txt');
  12. our $hash = {};
  13. our @lines;
  14. our %fold_md5;
  15. our %md5map;
  16. my $gstr;
  17. my $ref;
  18. print "Path to struct ...\n";
  19. open READ,"<:raw",'D:\a.txt';
  20. seek(READ, 2, 0);
  21. {
  22.     local $/ = "\x0d\x00\x0a\x00";
  23.     while ($line = <READ>)
  24.     {
  25.         $line =~s/\x0d\x00\x0a\x00//;
  26.         $gstr = encode('gbk', decode('utf16-le', $line));
  27.         if ( $gstr=~s/^FOLD: //i )
  28.         {
  29.             $ref = toStruct( $gstr );
  30.             push @lines, $gstr;
  31.         }
  32.         else
  33.         {
  34.             if ( $gstr=~s/^\s+(\d+) // )
  35.             {
  36.                 $ref->{$gstr} = $1;
  37.             }
  38.             else
  39.             {
  40.                 #nothing to do
  41.             }
  42.         }
  43.     }
  44. }
  45. close READ;
  46. print "Compare ...\n";
  47. compare();
  48. sub compare
  49. {
  50.     my @parts;
  51.     my $ref;
  52.     my $md5;
  53.     print "Getting md5 information ...\n";
  54.     open WRT, ">:raw", "D:\\md5map.txt";
  55.     for my $i ( 0 .. $#lines )
  56.     {
  57.         @parts = split(/[\/\\]/, $lines[$i]);
  58.         $ref = $hash;
  59.         grep { $ref = $ref->{$_} } ( @parts );   #将引用迭代到路径的最后一层
  60.         next if (! keys %{$ref} );               #如果没有下一层文件内容则略过
  61.         
  62.         $md5 = md5_hex( Dump($ref) );
  63.         $fold_md5{ $lines[$i] } = $md5;
  64.         print WRT $md5 ." ". encode('utf8', decode('gbk', $lines[$i])) ."\n";
  65.     }
  66.     close WRT;
  67.     for my $k ( keys %fold_md5 )
  68.     {
  69.         $md5 = $fold_md5{$k};
  70.         push @{ $md5map{$md5} }, $k;
  71.     }
  72.     my $info = { 'sizes' => 0,
  73.                  'folds' => 0,
  74.                  'files' => 0 };
  75.     print "Find same folder\n";
  76.     for my $k ( keys %md5map )
  77.     {
  78.         if ( $#{$md5map{$k}} > 0 )
  79.         {
  80.             @parts = split(/[\/\\]/, $md5map{$k}->[0] );
  81.             $info = { 'sizes' => 0, 'folds' => 0, 'files' => 0 };
  82.             $ref = $hash;
  83.             grep { $ref = $ref->{$_} } ( @parts );
  84.             Summarize( $ref, $info );
  85.             if ( $info->{sizes} > 1024*1024*100 ) #100MB
  86.             {
  87.                 print int($info->{sizes}/1024/1024) ."mb\n";
  88.                 print join("\n", @{$md5map{$k}} );
  89.                 print "\n\n";
  90.                 #dump_byPath( $md5map{$k}->[0] );
  91.             }
  92.         }
  93.     }
  94. }
  95. sub dump_byPath
  96. {
  97.     my $path = shift;
  98.     my $ref = $hash;
  99.     for my $e ( split(/[\/\\]/, $path) )
  100.     {
  101.         $ref = $ref->{$e};
  102.     }
  103.     print Dump( $ref );
  104. }
  105. #获取某个目录的文件大小总和
  106. sub Summarize
  107. {
  108.     my ($ref, $info) = (shift, shift);
  109.     for my $k ( keys %{$ref} )
  110.     {
  111.         if (ref( $ref->{$k} ) eq 'HASH')
  112.         {
  113.             $info->{folds} ++;
  114.             Summarize( $ref->{$k}, $info );
  115.         }
  116.         else
  117.         {
  118.             $info->{files} ++;
  119.             $info->{sizes} += $ref->{$k};
  120.         }
  121.     }
  122. }
  123. sub toStruct
  124. {
  125.     my $path = shift;
  126.     my @parts = split(/[\/\\]/, $path);
  127.     my $ref;
  128.     $ref = $hash;
  129.     for my $e ( @parts )
  130.     {
  131.         if ( not exists $ref->{$e} )
  132.         {
  133.             $ref->{$e} = {};
  134.         }
  135.         $ref = $ref->{$e};
  136.     }
  137.     return $ref;
  138. }
复制代码
输出示例:
  1. 117mb
  2. D:\Local\Dict\Youdao\Source\
  3. D:\Dupl\Dict\有道\
  4. 506mb
  5. D:\Dupl\设计软件\CoreldrawX5+资料\coreldraw x5\coreldraw x5\CGS15\
  6. D:\Program Files (x86)\Corel\CorelDRAW Graphics Suite X5\Setup\CGS15\
  7. 1503mb
  8. D:\3DGameProgramming\Figures\
  9. D:\Data\Books\Book IV\Figures\
  10. 249mb
  11. D:\3DGameProgramming\Figures\Chapter 8 Texturing\
  12. D:\Data\Books\Book IV\Figures\Chapter 8 Texturing\
复制代码
这酸爽,原来我D盘有几个G 的副本……
作者: happy886rr    时间: 2017-1-22 10:41

回复 18# 523066680
判断大文件可抽样md5,二分法抽样,这样误判几率小于1/pow(2,n),目前对大文件只能做抽样md5检测。
作者: CrLf    时间: 2017-1-22 16:07

回复 19# happy886rr


    既然都明确要对比两个文件了,直接用 memcmp 不是更直接吗
作者: 523066680    时间: 2017-1-22 22:34

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

回复 19# happy886rr


    也想过抽样。好在对于一般的硬盘,上G的文件也多不到哪里去。(先满足个人需求了 )
回老家了,爪机码字
作者: happy886rr    时间: 2017-1-23 01:53

回复 20# CrLf
这个函数不错嘛。
作者: happy886rr    时间: 2017-1-23 01:54

本帖最后由 happy886rr 于 2017-1-23 01:55 编辑

回复 21# 523066680
带个本本随地办公吧。不过,我觉得还是回老家有年味,城里都不热闹啊。




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2