Board logo

标题: [文件操作] 批处理如何提高效率:复制源文件夹中新近创建的文件至指定文件夹 [打印本页]

作者: alredstone    时间: 2022-5-17 18:03     标题: 批处理如何提高效率:复制源文件夹中新近创建的文件至指定文件夹

本帖最后由 alredstone 于 2022-5-18 12:36 编辑

=============== 以下为最近一次编辑的内容 ===============

目标:遍历每个文件获取其创建日期,并和指定日期进行比对,最后复制或跳过。

结论:继续求教更高执行效率的处理办法。


以下为讨论部分:

关于bat执行效率的问题,论坛里有较多讨论,@随风 版主也做了汇总 http://www.bathome.net/thread-4828-1-1.html 。 然而执行效率问题依旧是绕不过的坎。

在坛友 @idwma 的建议下,我尝试将多次调用 call 和 goto 改为 for 和 if 语句,因此也引入了 管道 和 findstr 。最终测试结果发现,两者对于1000个文件的处理,差异不大(3秒以内,重复三次)。

目前可获得的结论如下:重复调用 call 和 goto 与重复使用 管道 和 findstr ,执行效率相当。考虑到 call 和 goto 的使用有助于增加代码的可读性(尤其是对新手而言),在没有更好选择的情况下,建议使用 call 和 goto 。

依据坛友 @idwma 的建议,更改后的代码如下:
  1. :identify_copy
  2. setlocal enabledelayedexpansion
  3. set "tmpdrctry=D:\newfolder"
  4. set strttm=%time%
  5. for /f "tokens=* delims=" %%z in ('dir /s/b/a-d-h-s') do (
  6. for /f "tokens=1" %%a in ('dir "%%z" /tc ^|findstr /e "%%~xz"') do (
  7. set "fpath=%%z"
  8. set "fdrctry=%%~dpz"
  9. set "fcd=%%a"
  10. if "!fcd!" GEQ "2022/01/01" (call set "tmpdrctry2=%%fdrctry:%cd%=%tmpdrctry%%%" &xcopy "!fpath!" "!tmpdrctry2!" /v/s)
  11. )
  12. )
  13. echo Process started at %strttm%, ended at %time%.
  14. pause
复制代码
.



=============== 以下为原帖内容 ===============

操作目标:复制当前文件夹中2022年1月1日之后创建的文件至指定文件夹(D:\newfolder),须保持目录树。

使用代码如下,因多次调用 call 和 goto,导致执行效率很低(总文件数约5000个)。

求教:如何提高执行效率。
  1. :identify_copy
  2. set strttm=%time%
  3. set "tmpdrctry=D:\newfolder"
  4. for /f "tokens=* delims=" %%z in ('dir /s/b/a-d-h-s') do (
  5. set "fpath=%%z"
  6. set "fdrctry=%%~dpz"
  7. call :acqrdt
  8. )
  9. echo Process started at %strttm%, ended at %time%.
  10. pause
  11. :acqrdt
  12. for /f "skip=4 tokens=1" %%a in ('dir "%fpath%" /tc') do (
  13. set "fcd=%%a"
  14. call :dtprcss & goto :EOF
  15. )
  16. :dtprcss
  17. if "%fcd%" GEQ "2022/01/01" (goto copynf)
  18. goto :EOF
  19. :copynf
  20. call set "tmpdrctry2=%%fdrctry:%sdrctry%=%tmpdrctry%%%"
  21. xcopy "%fpath%" "%tmpdrctry2%" /v/s
  22. goto :EOF
复制代码

作者: idwma    时间: 2022-5-17 18:21

  1. xcopy . D:\newfolder /d:1-1-2022 /v/s
复制代码

作者: alredstone    时间: 2022-5-17 18:26

回复 2# idwma


    xcopy 的 /d 参数是修改日期,不是创建日期。
作者: idwma    时间: 2022-5-17 18:32

  1. robocopy . D:\newfolder /maxage:20220101 /s
复制代码

作者: alredstone    时间: 2022-5-17 18:39

回复 4# idwma


    robocopy 的 maxage 参数,也是修改日期
作者: idwma    时间: 2022-5-17 18:50

本帖最后由 idwma 于 2022-5-17 19:23 编辑
  1. setlocal enabledelayedexpansion
  2. set "tmpdrctry=D:\newfolder\"
  3. for /f "tokens=* delims=" %%z in ('dir /s/b/a-d-h-s') do (
  4. for /f "skip=4 tokens=1" %%a in ('dir "%%z" /tc') do (
  5.   if "%%a" GEQ "2022/01/01" (
  6.        set "fdrctry=%%~dpz"
  7.         set "fdrctry=!fdrctry:%cd%=!"
  8. mkdir "%tmpdrctry%!fdrctry!"
  9. copy "%%z" "%tmpdrctry%!fdrctry!"
  10.   )
  11. )
  12. )
复制代码

作者: alredstone    时间: 2022-5-17 21:31

本帖最后由 alredstone 于 2022-5-17 23:28 编辑

回复 6# idwma


    说明一下,因对单个文件 dir 的 /tc 结果有 6 行,上述代码直接使用的话会出错,需要添加 |findstr /e "%%~xz"   

    总的来说,非常棒的思路,降低了call、goto的调用。
作者: alredstone    时间: 2022-5-17 23:27

尴尬的问题出现了,因为 管道 和 findstr 的使用,最终执行效率并没有改善
作者: idwma    时间: 2022-5-18 14:12

回复 7# alredstone


    不加findstr也可以的吧, 出什么样的错?
作者: alredstone    时间: 2022-5-18 16:53

回复 9# idwma


    对单个文件 dir /tc 输出结果一共有 6 行(不计空行),skip=4 只能跳过前 3 行,接着会将 4~6 行的结果赋值给 %%a,即同一个文件会赋值 3 次,导致出错。

这也是我采用的代码中获取创建日期的时候写为 call :dtprcss & goto :EOF 的原因。

对单个文件 dir /tc 输出的信息如下:
  1. 驱动器属性
  2. 卷序号
  3. 文件所在目录
  4. 文件创建日期 时间 大小 文件名
  5. 目录内文件个数 大小
  6. 包含目录个数 卷剩余容量
复制代码

作者: qixiaobin0715    时间: 2022-5-18 17:03

6楼代码第5行难道是吃素的?不知楼主是否实际测试过。
作者: qixiaobin0715    时间: 2022-5-18 17:10

这种情况好像只能在源文本中测试,因为在备份文件中测试,创建时间就变成备份时的时间了。
作者: idwma    时间: 2022-5-18 17:22

第8行改一下应该不提示出错了吧
  1. mkdir "%tmpdrctry%!fdrctry!" 2>nul
复制代码

作者: alredstone    时间: 2022-5-18 17:26

回复 11# qixiaobin0715


    别着激动嘛

  if "%%a" GEQ "2022/01/01" 这句是将 %%a 的值与字符串 2022/01/01 进行比较。在本帖讨论的条件下,对于每个文件而言 %%a 会被赋值 3 次,即后面的命令也会重复 3 次。

即除了 创建时间 会被赋值给 %%a 外,目录内的文件数目录数同样也会被赋值给 %%a 并与字符串 2022/01/01 进行比较。因此会出错。

代码的鲁棒性(Robustness)也是一个需要考虑的因素。
作者: alredstone    时间: 2022-5-18 17:38

回复 13# idwma


      这样也可以吧,也是一种解决思路。
作者: alredstone    时间: 2022-5-18 17:45

回复 12# qixiaobin0715


    测试的时候将 dir /tc 改为 dir /tw (最后修改时间)就可以了。运行效率上不会有明显差别。
作者: qixiaobin0715    时间: 2022-5-19 08:51

回复 14# alredstone
我觉得你可能没有理解对。对于同一个文件来说,循环变量%%a可能会发生变化,而判断语句括号中的语句只会执行一次。而你所说的“错误”,就像 idwma 在13楼指出的那样,是mkdir命令的问题,有可能同一个文件夹中存在多个符合条件的文件,创建文件夹时有重名情况引起的。
作者: alredstone    时间: 2022-5-19 12:29

本帖最后由 alredstone 于 2022-5-19 12:58 编辑

回复 17# qixiaobin0715


    您说的没错,判断语句括号中的语句只会执行一次。然而,对 %%a 的赋值是 for /f "skip=4 tokens=1" %%a in ('dir "%%z" /tc') 完成的,是在 if 语句之前。

因为 %%a 会被赋值 3 次, 所以 if "%%a" GEQ "2022/01/01" 这句会被执行 3 次。而这 3 次比对,会导致代码的鲁棒性不佳(比如日期格式为 MM/DD/YYYY 的系统里,就会报错),这是我在纠结的地方。

所以说,不是我没有理解 @idwma 坛友的代码,而是您可能没有理解 cmd 对 for /f + if 的转译规则。

题外话,关于 mkdir 命令创建重名文件夹报错的问题,处理起来很简单,删掉 mkdir 行,将 copy 改为 xcopy 命令就可以了。当然,@idwma 坛友直接关闭报错的方式,更加简单粗暴。


.




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