Board logo

标题: [文本处理] 批处理怎么保留文件最高版本,然后把最高版本的版本号设置成1 [打印本页]

作者: 阿一呐法    时间: 2022-11-2 10:15     标题: 批处理怎么保留文件最高版本,然后把最高版本的版本号设置成1

文件夹下有软件生成的各个版本文件如下
      a.txt.1
      a.txt.2
      a.txt.3
....
      b.txt.4
      b.txt.10
      b.txt.15
...
      c.pro.22
      c.pro.50
      c.pro.51
....
      d.prt.4
      d.prt.100
      d.prt.201
....

类似的文件,怎么批处理后先保留最高版本,如上面文件执行后只保留的文件如下
      a.txt.3
....
      b.txt.15
...
      c.pro.51
....
      d.prt.201
....


然后把保留的最高版本号全部改成1,如下


      a.txt.1
....
      b.txt.1
...
      c.pro.1
....
      d.prt.1
....
作者: qixiaobin0715    时间: 2022-11-2 11:49

想不明白了,改了多次啦,还是楼主来试试吧。bat文件保存为ANSI编码:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "delims=" %%i in ('dir /b /a-d ^|findstr /e "\.[1-9][0-9]*"') do (
  4.     set m=%%~xi
  5.     set m=!m:.=!
  6.     if not "%%~ni"=="!str1!" (
  7.         if defined str1 ren "!str1!!str2!" "!str1!.1"
  8.         set /a n=m
  9.         set str1=%%~ni
  10.         set str2=%%~xi
  11.     ) else (
  12.         if !m! gtr !n! (
  13.             set /a n=m
  14.             del "!str1!!str2!"
  15.             set str2=%%~xi
  16.         ) else (
  17.             del "%%i"
  18.         )
  19.     )
  20. )
  21. ren "!str1!!str2!" "!str1!.1"
  22. pause
复制代码
未经测试,请楼主自测。
作者: hfxiang    时间: 2022-11-2 14:34

本帖最后由 hfxiang 于 2022-11-3 08:53 编辑

测试一下用gawk( http://bcn.bathome.net/tool/4.1.0/gawk.exe )来处置,以下脚本在win10下工作正常:
  1. @echo off
  2. cd /d "%~dp0"
  3. dir/s/b/a-d|gawk "match($0,/(^.+\\)([^\\]+)\.([0-9]+)$/,arr){++i;b[i]=arr[2];c[i]=arr[3];d[i]=$0;if(f[arr[2]]+0<arr[3]){f[arr[2]]=arr[3]}}END{for(j=1;j<=i;j++)if(!(f[b[j]]==c[j])){print \"del \" \"\\\"\" d[j] \"\\\"\"}}"|cmd.exe
  4. dir/s/b/a-d|gawk "match($0,/(^.+\\)([^\\]+)\.([0-9]+)$/,arr){print \"move /Y \\\"\" $0 \"\\\" \\\"\" arr[1] arr[2] \".1\" \"\\\"\"}"|cmd.exe
复制代码

作者: 阿一呐法    时间: 2022-11-2 15:31

回复 2# qixiaobin0715


    谢谢,测试有效,

    再请教一下,如果子目录这样的文件一起呢,需要怎么修改
作者: qixiaobin0715    时间: 2022-11-3 08:10

回复 4# 阿一呐法
想明白了也好改,看看3楼代码能否满足你的要求。
作者: WHY    时间: 2022-11-3 11:04

本帖最后由 WHY 于 2022-11-3 16:23 编辑
  1. @echo off
  2. PowerShell -c "dir -Literal '%~dp0' -Recurse | ?{ $_ -is [IO.FileInfo] -and $_.Extension -match '^\.[1-9]\d*$' } | sort{ 1 * $_.Extension.Trim('.') } | group{ $_.FullName -replace '\.\d+$' } | forEach{ $n = $_.Count - 1; for($i=0;$i -lt $n;$i++){ del -Literal $_.Group[$i].FullName -WhatIf } mv -Literal $_.Group[$n].FullName -Dest ($_.Name + '.1') -WhatIf }"
  3. pause
复制代码
WhatIf: 对目标“E:\Test\a.txt.1”执行操作“删除文件”。
WhatIf: 对目标“E:\Test\a.txt.2”执行操作“删除文件”。
WhatIf: 对目标“项: E:\Test\a.txt.3 目标: E:\Test\a.txt.1”执行操作“移动文件”。
WhatIf: 对目标“E:\Test\b.txt.4”执行操作“删除文件”。
WhatIf: 对目标“E:\Test\b.txt.10”执行操作“删除文件”。
WhatIf: 对目标“项: E:\Test\b.txt.15 目标: E:\Test\b.txt.1”执行操作“移动文件”。
WhatIf: 对目标“E:\Test\d.prt.4”执行操作“删除文件”。
WhatIf: 对目标“E:\Test\d.prt.100”执行操作“删除文件”。
WhatIf: 对目标“项: E:\Test\d.prt.201 目标: E:\Test\d.prt.1”执行操作“移动文件”。
WhatIf: 对目标“E:\Test\c.pro.22”执行操作“删除文件”。
WhatIf: 对目标“E:\Test\c.pro.50”执行操作“删除文件”。
WhatIf: 对目标“项: E:\Test\c.pro.51 目标: E:\Test\c.pro.1”执行操作“移动文件”。
请按任意键继续. . .

作者: WHY    时间: 2022-11-3 11:45

  1. @echo off & setlocal enabledelayedexpansion
  2. if "%~1" == "" (
  3.     for /f "tokens=1,3 delims=/" %%i in ('"%~f0" ARG ^| sort') do (
  4.         if /i "%%i" == "!f!" (
  5.             echo;del "!f!!e!"
  6.         ) else if defined f (
  7.             for /f "delims=" %%k in ("!f!") do echo;ren "!f!!e!" "%%~nxk.1"
  8.         )
  9.         set "f=%%i"
  10.         set "e=%%j"
  11.     )
  12.     if defined f (
  13.         for /f "delims=" %%k in ("!f!") do echo;ren "!f!!e!" "%%~nxk.1"
  14.     )
  15.     pause & exit
  16. ) else (
  17.     for /f "delims=" %%i in ('dir /b /a-d /s ^| findstr "\.[1-9][0-9]*$"') do (
  18.         for /f "delims=." %%j in ("%%~xi") do set "s=0000000000%%j"
  19.         echo;%%~dpni/!s:~-10!/%%~xi
  20.     )
  21. )
复制代码

作者: 阿一呐法    时间: 2022-11-3 11:45

本帖最后由 阿一呐法 于 2022-11-3 11:49 编辑

回复 6# WHY


    测试了,文件还在,并没有删除低版本,
作者: qixiaobin0715    时间: 2022-11-3 11:57

回复 8# 阿一呐法
你不会把del、ren命令前面的echo;删除吗。
作者: 阿一呐法    时间: 2022-11-3 13:07

回复 9# qixiaobin0715


    没注意这具,谢谢了
作者: xczxczxcz    时间: 2022-11-3 18:25

楼上各位的代码效率???
看一楼的文件极似 CREO|PROE的工程文件的各种零件日志的版本文件。若是,这种脚本可以集成到 软件的快捷键中。若楼主刚入门工程做一些基础简单的产品,上面的代码都可以了。若已是大拿,一个产品就成百上千个子零件,各位的脚本得优化优化了。不建议使用 if /else find/str group 之类低效的命令。
  若楼主真是这行业。可以这样,找出后缀为数字的文件,排序。然后把文件一个个直接改名(硬改),不要做任何判断。 若是集成到软件中,不要用"%~dp0",要用"%~1"
俺曾经用的:效率刚刚的。下面的示范不能直接用于你的工作中,需要按你的要求修改。 还有那个后缀 ’.1' 去掉更好。去掉后就是标准的模板文件名。若不是该行业,那当这是废话。
  1. ls ".\*" -r|?{!$_.PsIsContainer -and $_.Extension -match '\.\d+$'}|sort @{e={$_.Extension.Trim('.') -as [int]}}|%{mv -Literal $_.FullName -dest ($_.DirectoryName+'\'+$_.BaseName+'.1') -Force;
  2. }
复制代码
// 快捷键写法
  1. mapkey $F12 @SYSTEMpurge & del /q *.out *.sec.* *.m_p *.acc.* *.err *.inf.* \
  2. mapkey(continued) *.crc.* *.log.* *.lst *.out *.pic *.pls *error.* *log.xml trail.txt* \
  3. mapkey(continued) *.ers* current_session.pro* *.bak.* \
  4. mapkey(continued) & call X:\\XX\\XX\\删后缀序号.bat;
复制代码

作者: WHY    时间: 2022-11-3 21:24

回复 11# xczxczxcz


    "硬改"是个好办法,可以简化脚本。
我倒是觉得,既然用了管道,那么肯定不是特别在乎效率,管道与效率天生就是冤家。
有人很喜欢"管道一行流",还有人效率永远高于一切。
每个人习惯与认识不同,所用方法及思路就不同,仅此而已。
作者: WHY    时间: 2022-11-3 21:39

本帖最后由 WHY 于 2022-11-4 14:07 编辑

如果想要效率,那么sort可以不用、管道可以用 for 代替,代价是:脚本可能变长,变复杂。
  1. $path = $MyInvocation.MyCommand.Path -replace '\\[^\\]+$';  #当前脚本目录
  2. $hash = @{};
  3. $max  = @{};
  4. $files = dir -Literal $path -Recurse | ?{ $_ -is [IO.FileInfo] -and $_.Extension -match '^\.[1-9]\d*$' }
  5. $count = $files.Count;
  6. for( $i = 0; $i -lt $count; $i++ ){
  7.     $key = $files[$i].DirectoryName + '\' + $files[$i].BaseName;
  8.     $ext = 1 * $files[$i].Extension.Trim('.');
  9.     if( $hash[$key] -isNot [Collections.ArrayList] ){
  10.         $hash[$key] = [Collections.ArrayList]@();
  11.     }
  12.     [void]$hash[$key].Add( $files[$i].FullName );   #文件全名加入HashTable
  13.     if( $max[$key] -lt $ext ){ $max[$key] = $ext; } #最大版本号
  14. }
  15. forEach( $key In $hash.Keys ){
  16.     $maxFile = $key + '.' + $max[$key];             #最大版本号文件
  17.     if( $hash[$key].Count - 1 ){
  18.         $hash[$key].Remove( $maxFile );             #从数组移除
  19.         Remove-Item -Literal $hash[$key] -WhatIf;   #删除文件
  20.     }
  21.     Move-Item -Literal $maxFile -Dest ( $key + '.1' ) -WhatIf;  #移动(改名)
  22. }
  23. [Console]::ReadLine();
复制代码

作者: xczxczxcz    时间: 2022-11-4 09:44

回复 13# WHY

字典和哈希表还是有区别的(至少在偶的使用范畴中是区别开的)。
无论是先判断存内存中再删除 还是直接改名都要做相同次数的磁盘擦写处理。所以存内存判断再处理不可能更效率。

本层回复与该贴内容没有任何关系,属天马行空讨论。

字典的效率,偶深表怀疑。

用字典勿谈效率,要效率远离字典(若被CSDN之类‘科普文章’洗脑当偶没说)。少量数据字典有优势,大数据下整数的排序效率碾压字典。大数据下的字典类似BAT中的Findstr。大数据下要效率最好用哈希表或自定义带整型的数据类型,亲身经历大数据勿掉字典的坑中(现在的偶要求任何程序或脚本当动态数据量超过1000条时禁用字典,字典只用来存储一次性的数据,如表情包,还有html的节点属性查询和反射存储属性等简单场景)。
   另外如 -Unique 参数有去重功能,但这是一个不讲任何效率的参数。大量数据就会'死机',若去此参数改用哈希表包裹一下,几十万数据也哗啦一下就完成了。管道虽可以节流,但现在CPU强大,管道的性能损失也可以接受(大内存去管道更好)。可以照顾低内存用户(工程界里普通用户的机子内存一般都很小)。
   偶曾经的PC: 3570K+DDR3-16G+镁光初代固态;测字典多种数据量下的动态性能,1000/5000/10000/20000/50000/100000/200000/1000000,在1000内时效率还不错,5000以上没有性能,10000以上假死。但整数的SORT在100000量也是高效的。不同的硬件字典的效率临界区间可能不一样。

广告:
   .net 要效率每行都要琢磨(累,C++写则不用太多考虑性能但写得烦),偶的播放器(NET6写的,除一小部分用了指针)的性能与POT|SMPLAYER(C++) 毫不逊色(因内集成有开发帐号与各种网的解密不便发到网上)。


如有不同,还请见谅,’伟人‘说:实践是检验真理的唯一标准。任何理论不经过实践检验都是空洞的。
作者: WHY    时间: 2022-11-4 13:12

回复 14# xczxczxcz


   
字典和哈希表还是有区别的(至少在偶的使用范畴中是区别开的)。


没用到Dictionary,只用到HashTable和ArrayList
键名=路径加文件名,键值=数组
无论是先判断存内存中再删除 还是直接改名都要做相同次数的磁盘擦写处理。所以存内存判断再处理不可能更效率。

效率的提升不是体现在擦写磁盘(文件删除),而是体现在遍历这些文件、处理这些文件的中间过程。
说直白点,就是以空间换时间。
作者: WHY    时间: 2022-11-4 14:11

https://learn.microsoft.com/zh-c ... view=powershell-5.1
PowerShell脚本性能注意事项

如果认为MS官网资料也在说谎,那就没法了。
作者: WHY    时间: 2022-11-8 16:12

本帖最后由 WHY 于 2022-11-8 16:15 编辑

今天休息,对楼上几个脚本进行了一番性能测试。
1.测试环境
CPU:AMD A8-9600 3.10 GHz
内存:12.0 GB
硬盘:西数240G固态 + 西数1T机械
系统:Win10中文专业版 21H2 19044.2130
2.测试数据
下面脚本保存到 E:\Test\测试.bat,(E盘为机械硬盘)生成测试数据(3万个文件)
  1. @echo off
  2. for %%i in (Test1 Test2 Test3 Test4) do (
  3.     md %%i 2>nul
  4.     for %%j in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do (
  5.         for %%k in (txt prt pro drw asm mfg frm sec) do (
  6.             for /L %%x in (1 11 400) do (
  7.                 echo;%%j.%%k.%%x > "%%i\%%j.%%k.%%x"
  8.             )
  9.         )
  10.     )
  11. )
  12. pause
复制代码
3.测试方法
a.关闭系统杀毒软件;
b.在E:\Test目录下,分别对6、7、11、13楼脚本进行测试;
c.每个脚本测试完成,记录相应测试结果;
d.重启;
e.循环步骤a~d对6、7、11、13楼进行操作。
4.测试结果
6楼,CPU占用平均27.1% 内存占用99.4MB  磁盘使用最高1.8MB 脚本运行时间50.7s
  1. @echo off
  2. echo %time%
  3. PowerShell -c "dir -Literal '%~dp0' -Recurse | ?{ $_ -is [IO.FileInfo] -and $_.Extension -match '^\.[1-9]\d*$' } | sort{ 1 * $_.Extension.Trim('.') } | group{ $_.FullName -replace '\.\d+$' } | forEach{ $n = $_.Count - 1; for($i=0;$i -lt $n;$i++){ del -Literal $_.Group[$i].FullName } mv -Literal $_.Group[$n].FullName -Dest ($_.Name + '.1') }"
  4. echo %time%
  5. pause
复制代码
7楼,CPU占用平均28.5% 内存占用10.6MB  磁盘使用最高2.5MB 脚本运行时间83.5s
  1. @echo off & setlocal enabledelayedexpansion
  2. if "%~1" == "" (
  3.     echo !time!
  4.     for /f "tokens=1,3 delims=/" %%i in ('"%~f0" ARG ^| sort') do (
  5.         if /i "%%i" == "!f!" (
  6.             del "!f!!e!"
  7.         ) else if defined f (
  8.             for /f "delims=" %%k in ("!f!") do ren "!f!!e!" "%%~nxk.1"
  9.         )
  10.         set "f=%%i"
  11.         set "e=%%j"
  12.     )
  13.     if defined f (
  14.         for /f "delims=" %%k in ("!f!") do ren "!f!!e!" "%%~nxk.1"
  15.     )
  16.     echo !time!
  17.     pause & exit
  18. ) else (
  19.     for /f "delims=" %%i in ('dir /b /a-d /s ^| findstr "\.[1-9][0-9]*$"') do (
  20.         for /f "delims=." %%j in ("%%~xi") do set "s=0000000000%%j"
  21.         echo;%%~dpni/!s:~-10!/%%~xi
  22.     )
  23. )
复制代码
11楼,CPU占用平均22.6% 内存占用92.3MB  磁盘使用最高2.2MB 脚本运行时间139.1s
  1. @echo off
  2. echo %time%
  3. PowerShell "ls -Literal '%~dp0' -r|?{!$_.PsIsContainer -and $_.Extension -match '\.\d+$'}|sort @{e={ $_.Extension.Trim('.') -as [int]}}|%%{mv -Literal $_.FullName -dest ($_.DirectoryName+'\'+$_.BaseName+'.1') -Force;}"
  4. echo %time%
  5. pause
复制代码
13楼,CPU占用平均28.5% 内存占用103.4MB  磁盘使用最高2.8MB 脚本运行时间32.7s
  1. $t = get-Date;
  2. $path = $MyInvocation.MyCommand.Path -replace '\\[^\\]+$';  #当前脚本目录
  3. $hash = @{};
  4. $max  = @{};
  5. $files = dir -Literal $path -Recurse | ?{ $_ -is [IO.FileInfo] -and $_.Extension -match '^\.[1-9]\d*$' }
  6. $count = $files.Count;
  7. for( $i = 0; $i -lt $count; $i++ ){
  8.     $key = $files[$i].DirectoryName + '\' + $files[$i].BaseName;
  9.     $ext = 1 * $files[$i].Extension.Trim('.');
  10.     if( $hash[$key] -isNot [Collections.ArrayList] ){
  11.         $hash[$key] = [Collections.ArrayList]@();
  12.     }
  13.     [void]$hash[$key].Add( $files[$i].FullName );   #文件全名加入HashTable
  14.     if( $max[$key] -lt $ext ){ $max[$key] = $ext; } #最大版本号
  15. }
  16. forEach( $key In $hash.Keys ){
  17.     $maxFile = $key + '.' + $max[$key];             #最大版本号文件
  18.     if( $hash[$key].Count - 1 ){
  19.         $hash[$key].Remove( $maxFile );             #从数组移除
  20.         Remove-Item -Literal $hash[$key];   #删除文件
  21.     }
  22.     Move-Item -Literal $maxFile -Dest ( $key + '.1' );  #移动(改名)
  23. }
  24. ((get-Date) - $t).Totalseconds
  25. [Console]::ReadLine();
复制代码
有兴趣可以亲自试一下。




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