bat搭配工具遍历文件使用示例

说明

本文收集一些常用的bat搭配其它工具遍历文件,进行一些开发的辅助性操作或辅助性开发

目录

for循环index参数说明

当我们编写for循环遍历文件时,遍历参数%%i的可变形式如下:

  • %%~i - 删除引号("),扩充 %%i
  • %%~fi - 将 %%i 扩充到一个完全合格的路径名
  • %%~di - 仅将 %%i 扩充到一个驱动器号
  • %%~pi - 仅将 %%i 扩充到一个路径
  • %%~ni - 仅将 %%i 扩充到一个文件名
  • %%~xi - 仅将 %%i 扩充到一个文件扩展名
  • %%~si - 扩充的路径指含有短名
  • %%~ai - 将 %%i 扩充到文件属性
  • %%~ti - 将 %%i 扩充到文件的日期/时间
  • %%~zi - 将 %%i 扩充到文件的大小
    与路径相关的变式,可以是多种组合:
  • %%~dpi 显示盘符与路径,即父路径
  • %%~dpni 显示盘符、路径与文件名,不显示后缀
  • %%~dpnxi 显示盘符、路径与文件名,显示后缀

dir的匹配符

  • * 包含文件和文件夹
  • *.* 仅包含文件

实例

基本遍历

递归遍历文件

可使用for /ffor /r两种形式
for /f形式需要遍历其它目录时,可在前后添加pushd/pop进行,或在dir中的匹配符前输入目录路径

for /f %%i in ('dir /s /b *') do (
    echo %%i
)

for /r形式需要遍历其它目录时,只需将根目录参数%cd%替换成对应的目录路径或遍历即可

for /r %cd% %%i in (*) do (
    echo %%i
)
遍历一级文件夹下的文件

此时%%i只输出文件名。dir中的参数/a-d是将文件夹排除在外。

for /f %%i in ('dir /b /a-d *') do (
    echo %%i
)
遍历多种类型的文件

如同时递归bat、cpp、sh、lua这四种类型的文件:

for /f %%i in ('dir /s /b f:\batch_files\*.bat, f:\cpp_files\*.cpp, f:\bash_files\*.sh, f:\lua_files\*.lua') do ( 
  echo %%i 
)
递归遍历文件夹

使用/ad表明只遍历文件夹

for /f %%i in ('dir /s /b /ad *') do (
    echo %%i
)
遍历一级文件夹

使用for /d可直接遍历当前运行目录下的一级文件夹

for /d %%i in (*) do (
    echo %%i
)

使用for /f形式:

for /f %%i in ('dir /b /ad *') do (
    echo %%i
)
使用常量遍历

较简单的一种方法,则是写定部分特定路径,使用,空格分隔这些路径,直接通过for循环进行遍历。
这种方法可以经常使用。

@echo off
@REM 记得开启延迟变量
setlocal EnableDelayedExpansion
set aDirPaths=
set "aDirPaths=!aDirPaths!,ui\aiicons"
set "aDirPaths=!aDirPaths!,ui\items"
set "aDirPaths=!aDirPaths!,ui\rideicons"
set "aDirPaths=!aDirPaths!,ui\roleicons"
for %%i in (!aDirPaths!) do (
    echo %%i
)

文件操作

获取相对路径

替换符=前的变量仅能用%%包含。替换符前可写常量

@echo off
@REM 记得开启延迟变量
setlocal EnableDelayedExpansion
for /r %cd% %%i in (*.*) do (
    set szPath=%%i
    set szRelPath=!szPath:%cd%\=!
    echo !szRelPath!
)
拼接文件名

将文件名拼接到一个变量中,然后输出到控制台,获取拼接结果。
如下,遍历bat使用";"拼接。
在拼接操作中,需将set关键词后的所有代码使用双引号包含。

同理,可以扩展到拼接绝对路径、相对路径,仅拼接文件、仅拼接文件夹等

@echo off
setlocal EnableDelayedExpansion
for /f %%i in ('dir /s /b *.*') do (
    set "a=!a!;%%~nxi"
)
@REM 去掉第一个;
set a=!a:~1!
echo !a!
获取文件时间
@echo off
setlocal EnableDelayedExpansion
for /r %cd% %%i in (*) do (
    for %%a in (%%i) do set fileDate=%%~ta
    echo %%i !fileDate!
)

组合操作

分布式调用

查找工程中存在的tolua.bat,并调用

for /f %%i in ('dir /s /b tolua.bat') do (
  pushd %%~dpi
  call tolua.bat
  popd
)

搭配第三方工具

TexturePacker将png转换成ktx

递归遍历png文件,使用TexturePacker进行ktx和pvr的转换

@echo off
setlocal EnableDelayedExpansion
@REM 配置第三方工具TexturePacker的路径
set PATH_TEXTURE_PACKER=D:\TexturePacker\bin\TexturePacker.exe
set "szParams=!szParams! --format xml"
set "szParams=!szParams! --max-size 2048"
set "szParams=!szParams! --shape-padding 2"
set "szParams=!szParams! --border-padding 2"
set "szParams=!szParams! --disable-rotation"
set "szParams=!szParams! --algorithm MaxRects"
set "szParams=!szParams! --no-trim"
set "szParams=!szParams! --dither-fs-alpha"
set "aKtxParams=!szParams! --opt RGBA8888"
@REM 遍历当前目录存在的文件夹,调用自定义函数进行转换
for /d %%i in (*) do (
    echo %%i
    call :function_transform_texture_set "%%i"
)
goto :end
@REM -----------------------------------------------------------------------
@REM 将png转换成ktx的功能封装成一个函数
:function_transform_texture_set
if "%~1" == "" goto :eof
if exist %~1.png del /q %~1.png
if exist %~1.xml del /q %~1.xml
@REM 检查空文件夹
set szCmdOutput=
for /f "delims=" %%a in ('dir /a /b %~1') do (
    set "szCmdOutput=%%a"
)
if "!szCmdOutput!" == "" (
    echo %~1 is empty
    rd /q %~1
    del /q %~1.ktx
    del /q %~1_alpha.ktx
    goto :eof
)
@REM ktx
%PATH_TEXTURE_PACKER%  %~1 !aKtxParams! --sheet %~1.png --data %~1.xml
goto :eof
@REM -----------------------------------------------------------------------
:end
批量进行svn mv操作

批量进行svn mv操作,能保留svn的提交记录。
如下,将editor下所有的头文件和cpp文件,迁移到ui文件夹下

@echo off
setlocal EnableDelayedExpansion
for /f %%i in ('dir /b ui\*.*') do (
  set szSrcPurePath=editor\%%~ni
  set szDstPurePath=ui\%%~ni
  if exist "!szSrcPurePath!.h" svn mv !szSrcPurePath!.h !szDstPurePath!.h
  if exist "!szSrcPurePath!.cpp" svn mv !szSrcPurePath!.cpp !szDstPurePath!.cpp
)
对多个仓库进行svn cleanup

如某台打包机,配置了多个不同的仓库,而且有进行经常性的拉去代码和编译工作。在硬盘有可能出现不足时,可在jenkins上配置一份svn cleanup清理缓存,来定期减少仓库的总大小

@echo off
setlocal EnableDelayedExpansion
set aEnvPaths=
set "aEnvPaths=!aEnvPaths! C:\env1develop"
set "aEnvPaths=!aEnvPaths! C:\env1debug"
set "aEnvPaths=!aEnvPaths! C:\env1release"
set aEnvPaths=!aEnvPaths:~1!
for %%p in (!aEnvPaths!) do (
  svn cleanup %%p --include-externals
  svn cleanup %%p --vacuum-pristines --include-externals
)

辅助编写代码

编写xml文件

例如,递归遍历将多个文件夹中的png文件,用相对路径写入Qt的qrc配置文件中

注意:qrc实际为xml文件,语法上存在大量的"<"、">"符号,写入文件时,需要添加"^"进行转义

@echo off
setlocal EnableDelayedExpansion
@REM 设置需要遍历的所有文件夹
set aDirPaths=
set "aDirPaths=!aDirPaths!,ui\aiicons"
set "aDirPaths=!aDirPaths!,ui\items"
set "aDirPaths=!aDirPaths!,ui\rideicons"
set "aDirPaths=!aDirPaths!,ui\roleicons"
set aDirPaths=!aDirPaths:~1!
set fileOutput=res.qrc
if exist "!fileOutput!" del /q "!fileOutput!"
@REM 第一次写入文件使用">",写入xml的标签头
(
  echo ^<RCC^>
  echo     ^<qresource prefix="/res"^>  
) > "!fileOutput!"
@REM 遍历文件写入
for %%p in (!aDirPaths!) do (
  set szDirPath=%%p
  @REM 将Windows的文件分隔符"\"替换成正斜杠"/"
  set szQrcPrefix=!szDirPath:\=/!
  for /f %%i in ('dir /b !szDirPath!\*.png') do (
    @REM 写入配置,并增加tab以适配格式
    echo         ^<file^>!szQrcPrefix!/%%i^</file^> >> "!fileOutput!"
  )
)
@REM 写入xml的标签尾
(  
  echo     ^</qresource^>
  echo ^</RCC^>
) >> "!fileOutput!"
编写markdown中的无序列表

递归遍历文件,编写所需要跳转的文件映射在markdown中的无序列表。运行后,从控制台复制输出结果到markdown中。

@echo off
setlocal EnableDelayedExpansion
for /r %%i in (*.lua) do (
  set szPath=%%i
  set szRelPath=!szPath:%cd%\=!
  echo * [%%~nxi]^(./!szRelPath!^)
)
生成WinMerger文件

对两个目录结构相同的大型工程目录,需要大量对比某些文件夹,但又不需要在全部比较时,可以编写个遍历脚本来批量生成,统一保存成WinMerge文件后,方便日后直接双击打开,无需再麻烦地拖动文件夹来对比。

@echo off
setlocal EnableDelayedExpansion
set OUTPUT_PATH=F:\WinMergeProjectsOfMiniWorld
@REM 指定两个工程的路径
set aEnvPaths=
set "aEnvPaths=!aEnvPaths!,C:\trunk\env1ed"
set "aEnvPaths=!aEnvPaths!,F:\trunk\env1oversea"
set aEnvPaths=!aEnvPaths:~1!
@REM 指定需要WinMerge的部分文件夹
set aSubPaths=
set "aSubPaths=!aSubPaths!,bin"
set "aSubPaths=!aSubPaths!,bin\res"
set "aSubPaths=!aSubPaths!,bin\res\ui"
set "aSubPaths=!aSubPaths!,source\client"
set "aSubPaths=!aSubPaths!,source\server"
set aSubPaths=!aSubPaths:~1!
if not exist %OUTPUT_PATH% mkdir %OUTPUT_PATH%
for %%i in (!aEnvPaths!) do (
  for %%j in (!aEnvPaths!) do (
    for %%k in (!aSubPaths!) do (
      set szEnvLeft=
      set szEnvRight=
      call :function_getFolderNameFromPath szEnvLeft "%%i"
      call :function_getFolderNameFromPath szEnvRight "%%j"
      set szLeftPath=%%i\%%k
      set szRightPath=%%j\%%k
      set szFolderName=
      call :function_getFolderNameFromPath szFolderName "!szLeftPath!"
      set "szOutputPath=%OUTPUT_PATH%\!szFolderName! !szEnvLeft! - !szEnvRight!.WinMerge"
      @REM echo szLeftPath = !szLeftPath!
      @REM echo szFolderName = !szFolderName!
      call :localFunction_createWinMergeProject "!szOutputPath!" "!szLeftPath!" "!szRightPath!"
    )
  )
)
@REM -----------------------------------------------------------------------
@REM 从路径中获取文件名
:function_getFolderNameFromPath
set "szPath=%~2"
if "!szPath!" == "" (
  echo [ERROR] path empty
  goto :eof
)
if not exist "!szPath!" (
  echo [ERROR] not exist: !szPath!
  goto :eof
)
pushd "!szPath!"
for /f "delims=" %%i in ('cd') do (
  set szFolderName=%%~nxi
)
popd
set "%~1=!szFolderName!"
goto :eof
@REM -----------------------------------------------------------------------
@REM 创建WinMerge文件
:localFunction_createWinMergeProject
set "szOutputPath=%~1"
set "szLeftPath=%~2"
set "szRightPath=%~3"
set "szFileType=%~4"
if "!szOutputPath!" == "" (
  echo [ERROR] output path empty
  goto :eof
)
if "!szLeftPath!" == "" (
  echo [ERROR] left path empty
  goto :eof
)
if "!szRightPath!" == "" (
  echo [ERROR] right path empty
  goto :eof
)
if "!szLeftPath!" == "!szRightPath!" (
  @REM echo [ERROR] both paths equal
  goto :eof
)
if not exist "!szLeftPath!" (
  @REM echo [ERROR] not exist: !szLeftPath!
  goto :eof
)
if not exist "!szRightPath!" (
  @REM echo [ERROR] not exist: !szRightPath!
  goto :eof
)
if "!szFileType!" == "" (
  set szFileType=*.*
)
if exist "!szOutputPath!" del /q "!szOutputPath!"

(
  echo ^<?xml version="1.0" encoding="UTF-8"?^>
  echo ^<project^>
  echo  ^<paths^>
  echo      ^<left^>!szLeftPath!^</left^>
  echo      ^<right^>!szRightPath!^</right^>
  echo      ^<filter^>!szFileType!^</filter^>
  echo      ^<subfolders^>1^</subfolders^>
  echo      ^<left-readonly^>0^</left-readonly^>
  echo      ^<right-readonly^>0^</right-readonly^>
  echo  ^</paths^>
  echo ^</project^>
) >!szOutputPath!
goto :eof
@REM -----------------------------------------------------------------------

相对于其它语言的代码,有关遍历文件的都可以自己配置来添加常量数组、常量字符串数组、代码等不同功能的文本

VSCode使用一键输入代码进行快速编写

使用VSCode时,可以通过配置Snippets来完成一键输入的效果,提高编写效率。
下方贴出基本遍历的配置代码,供大家方便复制使用:

{
    "for_f_iterate_files_recursively" : {
        "scope": "bat",
        "prefix": "forffr",
        "body": [
            "for /f %%i in ('dir /s /b *') do (",
            "  echo %%i",
          ")",
        ],
        "description": "递归遍历文件"
    },

    "for_r_iterate_files_recursively" : {
        "scope": "bat",
        "prefix": "forrfr",
        "body": [
            "for /r %cd% %%i in (*) do (",
            "  echo %%i",
          ")",
        ],
        "description": "递归遍历文件"
    },

    "for_f_iterate_files" : {
        "scope": "bat",
        "prefix": "forff",
        "body": [
            "for /f %%i in ('dir /b *') do (",
            "  echo %%i",
          ")",
        ],
        "description": "遍历一级文件"
    },

    "for_f_iterate_dirs_recursively" : {
        "scope": "bat",
        "prefix": "forfdr",
        "body": [
            "for /f %%i in ('dir /s /b /ad *') do (",
            "  echo %%i",
          ")",
        ],
        "description": "递归遍历文件夹"
    },

    "for_d_iterate_dirs" : {
        "scope": "bat",
        "prefix": "ford",
        "body": [
            "for /d %%i in (*) do (",
            "  echo %%i",
          ")",
        ],
        "description": "遍历一级文件夹"
    },

    "for_d_iterate_dirs" : {
        "scope": "bat",
        "prefix": "forfd",
        "body": [
            "for /f %%i in ('dir /b /ad *') do (",
            "  echo %%i",
          ")",
        ],
        "description": "遍历一级文件夹"
    },
}

像其它如Sublime的工具,也可以自行配置来方便编写

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容