Board logo

标题: [文件操作] [已解决]批处理怎样靠CRC32校验来删除文件? [打印本页]

作者: necnec1    时间: 2012-12-1 14:26     标题: [已解决]批处理怎样靠CRC32校验来删除文件?

0000aaca  3337424.pdf
0000b4be  2276006.pdf
0000b4c3  2490533.pdf
0000b4c3  543543543543534-----2917205.pdf
0000b9b0  1495391.pdf
0000bf34  2377773.pdf
0000bf34  567676868678-----3015760.pdf

在F盘根目录下有个AA文件夹  里面有300多万个文件  我建了一个文本文件 按照左边是CRC32校验码  右边是文件名的格式

想把左边CRC32校验码是一样的重复文件 提取出来 只保留一组
比如
0000bf34  2377773.pdf
0000bf34  567676868678-----3015760.pdf
这2组 0000bf34出现2次 说明这2个文件是重复的   删除其中一个文件到回收站  千万别彻底删除了 文件我还有用

需要注意的是  文本文件只有CRC校验码和文件名 并没有路径  只能保证文件名不出现重复 文件夹下还有N多的子文件夹

请会的朋友帮忙 谢谢~
作者: ShadowFiend    时间: 2012-12-1 17:26

  1. @echo off
  2. md newfolder 2>nul
  3. set "crc32sum=%~dp0crc32sum.exe"
  4. set "#tmp=%~dp0#tmp"
  5. cd /d D:\aa
  6. (for /f "delims=" %%i in ('dir /b/a-d') do (
  7.    %crc32sum% %%i
  8. ))>%#tmp%
  9. for /f "tokens=1,2" %%i in (%#tmp%) do (
  10.   if not defined %%j (
  11.     set %%j=flag
  12.     copy /y "%%i" "%~dp0newfolder"
  13.   )
  14. )
  15. del "%#tmp%" /q
  16. start %~dp0newfolder
  17. pause
复制代码
需要工具crc32sum.exe

http://code.kliu.org/misc/hashutils/
作者: BAT-VBS    时间: 2012-12-1 20:52

回复 2# ShadowFiend


    楼主要求删除到回收站
作者: ShadowFiend    时间: 2012-12-1 21:13

回复 3# BAT-VBS
    大概那个意思就行了,那么死板干嘛
作者: BAT-VBS    时间: 2012-12-1 21:17

回复 4# ShadowFiend


    这个需要问楼主
删除其中一个文件到回收站  千万别彻底删除了 文件我还有用

作者: necnec1    时间: 2012-12-1 21:26

回复 4# ShadowFiend
回复楼主  你这个批处理貌似从新校验CRC32   我文件有将近300万个  时间太长了

再说 我已经校验好了
0000aaca  3337424.pdf
0000aaca就是3337424.pdf CRC32校验码
我已经放到了一个文本文件里  现在就是要处理这个文本文件

关键就是这个文本文件没有路径 不好删除
我的愿望是先处理这个文本文件  得到重复的文件名  然后遍历文件夹 去删除同名的文件
作者: ShadowFiend    时间: 2012-12-1 22:10

回复 6# necnec1


     子文件夹有重名的你也删?300万个文件确认没有重名的?
作者: necnec1    时间: 2012-12-1 22:30

文件名我已经重命名过了 基本可以排除有重名
就是子文件夹有点多
作者: BAT-VBS    时间: 2012-12-1 22:34

回复 6# necnec1
  1. @echo off
  2. for /f "tokens=1-2" %%a in (a.txt) do (
  3.     if not defined _%%a (
  4.         set _%%a=1
  5.     ) else (
  6.         echo 删除%%b
  7.     )
  8. )
  9. pause
复制代码
是这个意思吗?
作者: necnec1    时间: 2012-12-1 23:04

楼上的代码有那么点意思了  不过还有点问题
删除的内容只在CMD窗口下显示  不能写入到文件
干脆直接把重复的那一行直接删除吧
反正CRC32的校验码都是固定的前8位  只要前8位字符是一样的  重复的行都删除 只保留一行就行了
作者: weichenxiehou    时间: 2012-12-2 10:23

现在的问题是你那些文件不是在同一目录下,这个是比较困难和耗时的部分。
我的想法是:先将要删除的文件名输出到一个文本a.txt,再将所有文件的完整路径输出到一个文本b.txt,然后用findstr根据这两个文件将需要删除的文件的完整路径取到另一个文本c.txt,最后用for/f循环删除所有需要删除的文件,虽然在多数情况下,临时文件是不推荐的,但这种情况下用临时文件应该是比较好的方式。
  1. @echo off
  2. ::将下一行的crc32.txt替换为实际的文件名
  3. (for /f "tokens=1*" %%a in (crc32.txt) do (
  4.   if defined %%a (echo,%%b) else set %%a=1
  5. ))>a.txt
  6. dir /s /b /a-d>b.txt
  7. findstr /i /g:a.txt b.txt>c.txt
  8. for /f "delims=" %%a in (c.txt) do del /a "%%a"
  9. del a.txt b.txt c.txt
复制代码
将crc32的校验结果文件和本bat文件一起放到要处理的文件夹的顶层目录,然后运行这个批处理即可。

必须满足:
1.校验结果文件格式要工整,每一行第一列必须为校验码,空格或tab后为文件名;
2.任何子目录不能取名为校验结果文件中的任何一个文件名;
作者: BAT-VBS    时间: 2012-12-2 10:30

回复 10# necnec1


加个简单的重定向就行了,这是基础知识。
  1. @echo off
  2. for /f "tokens=1-2" %%a in (a.txt) do (
  3.     if not defined _%%a (
  4.         set _%%a=1
  5.     ) else (
  6.         >>b.txt echo 删除%%b
  7.     )
  8. )
  9. pause
复制代码

作者: BAT-VBS    时间: 2012-12-2 10:36

回复 11# weichenxiehou


这么简单的问题是不用临时文件滴
  1. @echo off
  2. for /f "tokens=1-2" %%a in (a.txt) do (
  3.     if not defined _%%a (
  4.         set _%%a=1
  5.     ) else (
  6.         for /f "delims=" %%h in ('dir /s /b /a-d %%b') do (
  7.             del /f /a "%%h"
  8.         )
  9.     )
  10. )
复制代码

作者: weichenxiehou    时间: 2012-12-2 10:59

回复 13# BAT-VBS
如果文件名中有空格怎么办?每一行都dir /s,300万个文件,你可以去试试效率,写批处理并不仅仅是能够实现就好,还需要考虑效率和特殊情况滴。
作者: BAT-VBS    时间: 2012-12-2 11:00

回复 14# weichenxiehou


    只有重复的行才会dir /s,至于到底哪个效率高,就只能等楼主的测试结果了。
作者: BAT-VBS    时间: 2012-12-2 11:02

回复 14# weichenxiehou


    空格的问题很容易,稍微改改就行了,一行代码都不用增加,看楼主的实际需求了。
作者: ShadowFiend    时间: 2012-12-2 11:52

回复 11# weichenxiehou
    支持这种用法和用临时文件。300万不是小数目,考虑到效率问题,用临时文件是正确的选择。而不是所谓的"那么简单"
作者: BAT-VBS    时间: 2012-12-2 12:41

回复 17# ShadowFiend


    在得到楼主的测试数据之前,都是瞎猜。
作者: necnec1    时间: 2012-12-2 13:18

BAT-VBS  你这个批处理 我估计了一下 大概要5个小时 太猛了
半个小时 大概才整理出2万左右
我估算了一下 我这个好像总共有20万左右的重复文件

weichenxiehou 的办法貌似没办法进行   先将要删除的文件名输出到一个文本a.txt  我没有办法得到
再将所有文件的完整路径输出到一个文本b.txt  这个可以  我用Everything导出就可以了

还有::将下一行的crc32.txt替换为实际的文件名 这一句  这个CRC32.TXT是个什么东西   和 A.TXT 是什么关系 要这个文件做什么?
作者: weichenxiehou    时间: 2012-12-2 13:39

回复 19# necnec1
擦,lz要不要这么可爱,那些原理是解释我批处理代码的实际运作方式的,不关你的事啊,你不是有一个存放了校验码和文件名的文本吗,把那个crc32.txt改成那个文本的真实名字就可以了。
作者: necnec1    时间: 2012-12-2 13:41

好滴 帅哥 我试试
作者: BAT-VBS    时间: 2012-12-2 14:05

回复 19# necnec1


    好的,你先试试11楼的方案,如果仍然太慢的话,我再用另外一个思路帮你写代码。
作者: necnec1    时间: 2012-12-2 19:14

weichenxiehou  你那个批处理不错  就是能稍微改进一下  我刚测试了一下你的批处理 你那个文件删的没影了  能否删除到回收站  重复的文件我还要 还得比对一下
作者: BAT-VBS    时间: 2012-12-2 20:32

回复 23# necnec1


    大概需要执行多长时间?
作者: weichenxiehou    时间: 2012-12-2 20:55

回复 23# necnec1
何必删除到回收站那么麻烦,不同的系统回收站的路径还不一样,建立一个备份文件夹,把要删除的文件移动到这里不就行了。
  1. @echo off
  2. md backup 2>nul
  3. ::将下一行的crc32.txt替换为实际的文件名
  4. (for /f "tokens=1*" %%a in (crc32.txt) do (
  5.   if defined %%a (echo,%%b) else set %%a=1
  6. ))>a.txt
  7. dir /s /b /a-d>b.txt
  8. findstr /i /g:a.txt b.txt>c.txt
  9. for /f "delims=" %%a in (c.txt) do  move /y "%%a" backup\
  10. del a.txt b.txt c.txt
复制代码

作者: necnec1    时间: 2012-12-2 21:01

BAT-VBS   你的批处理 我从中午12点半开始 到下午的6点半 执行了大概6万左右
刚开始速度还没到那种无法忍受的地步 到后来 几乎是越执行越慢 到6点半 果断放弃
如果20万的重复量的话  不知道1天跑不跑的完
作者: necnec1    时间: 2012-12-2 21:04

weichenxiehou的代码 没敢用300万文件试 先用4000左右的文件测试 非常快 用他的批处理 几秒之内搞定 效率不错 打算今晚大规模用
作者: necnec1    时间: 2012-12-2 21:10

300万的PDF文件 数量巨大 本来想用查找重复的软件弄弄 结果动辄就要起码1天的时间  想想放弃了  想走点捷径 看看批处理 貌似没太好的办法 结果用了一个自己都觉得恶心的办法 用WINRAR来提取CRC32的校验码 300万的文件 用了6个小时 还凑合 基本符合预期 配合批处理我估计 应该很快
这些要谢谢BAT-VBS 和weichenxiehou 2个兄弟大力帮忙
作者: namejm    时间: 2012-12-2 22:11

25楼的代码大致能解决问题
但是,如果环境比较复杂的话,还是会误删文件的
问题出在这一句:
  1. findstr /i /g:a.txt b.txt>c.txt
复制代码
因为 findstr 是部分支持正则表达式的
用 /g:a.txt 从a.txt文件中获取到的字符串几乎每行都含有点号,而点号能匹配任意一个字符
形如 1.pdf 的名称是能和 1Xpdf 这样的字符串匹配的
当 b.txt 中字符串的组成规律足够复杂,出错的几率会很高
另外,从a.txt中获取到的字符串,findstr无法限定它们是否位于绝对路径的最后一层,并且不能限定之后是否包含其他字符
搜索 1.pdf 的时候,能匹配到 d:\test\11.pdf ,也能匹配到 d:\test1.pdf\a.pdf
所以,需要对a.txt中的字符串做进一步处理
当 findstr /g:a.txt 的时候,使得那些特殊字符失去正则意义而回归普通字符的含义,且能限定该字符串等于完整路径的最后一层
一个可行的办法是:
把 a.txt 中每行字符的点号用 \. 来替换,行首添加字符 ^ ,行尾添加字符$
需要用 gsed.exe 进行处理
在25楼的基础上修改如下:
  1. @echo off
  2. md backup 2>nul
  3. ::将下一行的crc32.txt替换为实际的文件名
  4. (for /f "tokens=1*" %%a in (crc32.txt) do (
  5.   if defined %%a (echo,%%b) else set %%a=1
  6. ))>a.txt
  7. gsed "s/\./\\./g;s/^/\\\\/;s/$/\$/" a.txt>b.txt
  8. dir /s /b /a-d>c.txt
  9. findstr /i /g:b.txt c.txt>d.txt
  10. for /f "delims=" %%a in (d.txt) do  move /y "%%a" backup\
  11. del a.txt b.txt c.txt d.txt
复制代码
gsed 可以去这里下载:http://www.bathome.net/thread-16975-1-1.html
作者: Batcher    时间: 2012-12-2 22:24

在29楼代码的基础上,改用gawk来判断重复文件,提高效率。
  1. @echo off
  2. md backup 2>nul
  3. ::将下一行的crc32.txt替换为实际的文件名
  4. gawk "{a[$1]++;if(a[$1]>1)print $2}" crc32.txt >a.txt
  5. gsed "s/\./\\./g;s/^/\\\\/;s/$/\$/" a.txt>b.txt
  6. dir /s /b /a-d>c.txt
  7. findstr /i /g:b.txt c.txt>d.txt
  8. for /f "delims=" %%a in (d.txt) do  move /y "%%a" backup\
  9. del a.txt b.txt c.txt d.txt
复制代码
gsed 可以去这里下载:http://www.bathome.net/thread-16975-1-1.html
gawk 可以去这里下载:http://www.bathome.net/thread-1114-1-1.html
作者: weichenxiehou    时间: 2012-12-2 22:36

回复 29# namejm
传说中的前站长namejm居然现身了,罕见罕见!jm一向以严谨著称,见识了。。。
要规避findstr搜索字符串的特殊字符,是不是可以用/l参数?一个文件名包含在另一个文件名中的情况确实需要考虑,惭愧惭愧。。。
  1. @echo off
  2. md backup 2>nul
  3. ::将下一行的crc32.txt替换为实际的文件名
  4. (for /f "tokens=1*" %%a in (crc32.txt) do (
  5.   if defined %%a (echo,\%%b) else set %%a=1
  6. ))>a.txt
  7. dir /s /b /a-d>b.txt
  8. findstr /il /g:a.txt b.txt>c.txt
  9. for /f "delims=" %%a in (c.txt) do  move /y "%%a" backup\
  10. del a.txt b.txt c.txt
复制代码

作者: cjiabing    时间: 2012-12-2 22:52

300万的文件要累死机器,刚开始很快,后面空间不足了当然慢了,如果有必要可以分成几部分处理。
作者: namejm    时间: 2012-12-2 22:56

呵呵,如果真要提高效率的话
还是建议从获取校验码的那一步做起
用专门的命令行工具来生成含绝对路径的校验文件
剩下的事情就是对生成的校验文件根据验证码进行处理了
可以用专门的命令行工具filemd5.exe或fsum.exe来生成带绝对路径的校验码文件
作者: namejm    时间: 2012-12-2 23:02

回复 31# weichenxiehou
如果只用findstr来做的话,还是需要对a.txt做特殊处理
比如在生成a.txt的时候,在每行行首都添加反斜杠 \
另外,还要给 findstr 添加 /e 参数
这样才能保证获取的字符串被限定在完整路径的最后一层,且等于最后一层的字符串
还需要特别注意的是,在 findstr 之前需要要在a.txt的最后一行添加一个空行
以防止 findstr /e 时不能获取到 a.txt 中的最后一行非空字符串
作者: Batcher    时间: 2012-12-3 00:32

回复 32# cjiabing


    你可以分别针对29楼和30楼的代码分析一下,看看哪里会导致空间(假设你指的是内存)问题。
作者: cjiabing    时间: 2012-12-3 20:37

回复 35# Batcher


    你显然不知道我回答问题的出发点在那里,我针对的是26楼说的情况,而非29楼和30楼。楼主这么说:
BAT-VBS 你的批处理 我从中午12点半开始 到下午的6点半 执行了大概6万左右
刚开始速度还没到那种无法忍受的地步 到后来 几乎是越执行越慢 到6点半 果断放弃
如果20万的重复量的话 不知道1天跑不跑的完
为什么执行的效率越来越差?我不是从代码执行效率来说的,而仅仅是从电脑使用操作的角度来说的。我只能简单地说内存资源耗尽了,空间不足了,你真要我说个子丑寅卯来我也无能为力了,因为我非计算机专业,仅能从我十年使用电脑的经验做出这样粗浅的判断。
作者: necnec1    时间: 2012-12-4 00:51

weichenxiehou最后的代码 出现了和BAT-VBS 类似的瓶颈   都是6个多小时查找到6万左右 跑起来相当费力  CPU占用50% 内存只耗费不到19M  感觉不像是内存的问题 我是2G的内存 5300的U  应该还凑合吧
是不是CMD本身的限制
namejm 版主改写的批处理貌似运行了一闪而过  只有一个backup文件夹 里面没东西 软件也下了 是哪有问题?
作者: necnec1    时间: 2012-12-4 00:57

我现在是这么一个想法
文本文件A 内容如下
3eb27c6f  8026844.pdf
e259ce1a  8026845.pdf
e259ce1a  8026644.pdf
d85ec019  8026847.pdf
e84f9de0  8026848.pdf
我再弄一个文本文件B   将所有PDF的完整路径和名称用Everything导出来

用文本文件A 和文本文件B去做比较  得出重复文件的完整路径和名称  然后转移保存到文件夹 岂不是更快
作者: Batcher    时间: 2012-12-4 12:10

回复 37# necnec1


    不要双击运行。打开一个CMD窗口,去运行那个bat文件,看看报错信息是什么。
作者: Batcher    时间: 2012-12-4 12:10

回复 36# cjiabing


    从37楼来看,不是内存问题。
作者: weichenxiehou    时间: 2012-12-4 20:38

本帖最后由 weichenxiehou 于 2012-12-4 20:46 编辑

回复 37# necnec1
我估计是因为用set定义了太多的变量,后面用if defined在如此之多的变量里面查找会比较吃力,可以先用sort排序,然后用一个变量来比较。
  1. @echo off&setlocal enabledelayedexpansion
  2. md backup 2>nul
  3. ::将下一行的crc32.txt替换为实际的文件名
  4. (for /f "tokens=1*" %%a in ('sort crc32.txt') do (
  5.   if %%a=="!str!" (echo,\%%b)
  6.   set "str=%%a"
  7. ))>a.txt
  8. dir /s /b /a-d>b.txt
  9. findstr /ile /g:a.txt b.txt>c.txt
  10. for /f "delims=" %%a in (c.txt) do  move /y "%%a" backup\
  11. del a.txt b.txt c.txt
复制代码
亲测,a.txt不额外加一空行不会出现jm说的最后一行不会当成搜索字符串的问题。
作者: BAT-VBS    时间: 2012-12-5 11:46

回复 41# weichenxiehou


    是不是你用的操作系统跟他的不一样啊?是XP吗,还是Win7啊?
作者: weichenxiehou    时间: 2012-12-5 19:09

回复 42# BAT-VBS
“他”是指谁?namejm?我用的win7,难道xp下面必须要在最后加空行,不知,我最开始用电脑就是从win7开始的。




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