找回密码
 立即注册
首页 业界区 安全 Windows bat 脚本学习记录

Windows bat 脚本学习记录

频鹏凶 昨天 09:16
学习这个古老的技能是因为最近 2024.8.28 优化 Windows build 脚本遇到了一些不懂的内容,故而想要系统的进行学习并记录(权当技术沉淀)
参考文献:《Windows 命令行详解手册》第二版第三章
由于 Windows 命令不区分大小写,故而可能出现大小写混用问题。另外命令行有些操作较为反常识,使用时还是多借助 AI 和小测例搞清楚。
回显

windows bat 脚本本质上就是一行行的在控制台上输入命令,导致此执行脚本的时候就会有一长串的输出,干扰了我们观察结果。所以通常 bat 脚本的第一行有 @echo off 来关闭回显。
这里在行首加上 @ 表示关闭此行的回显。@echo off 的意思是关闭后续回显,此行的回显也不出现。
当脚本运行出现问题时,echo on 或删掉/注释掉第一行,根据回显的输出诊断脚本出错位置。
特殊用法
  1. :: 查看 echo 状态是开启还是关闭
  2. echo
  3. :: 显示空行
  4. echo .
复制代码
注释


  • 如果这行注释是提示信息,我们可以使用 rem 进行注释(在 echo on 时会回显)
  • 如果这行注释只是解释脚本的功能、创建时间、作者信息等可以用 @REM 或 :: 进行注释(始终不回显)
标题

TITLE 命令即可,在执行长耗时的任务时,通过切换 TITLE 了解当前处于什么阶段。
  1. TITLE 学习 Windows Shell 中
复制代码
颜色

数值颜色数值颜色0黑8灰1蓝9淡蓝2绿A淡绿3浅绿B浅水绿4红C浅红5紫D淡紫6黄E淡黄7白F亮白color [背景颜色数值][文字颜色数值]例如 color 21 表示绿色背景蓝色文字。默认是黑背景白字即 color 07。注意这两个数值不能一致,否则设置不生效(合理)。
变量

有些变量(包括系统和用户环境变量)在 Shell 中有特殊用途,例如 path、computername、homedrive。另外还有 errorlevel, 一个用于跟踪最近使用命令的退出代码。

  • 返回 0 表示正常执行
  • 返回 1 表示通常错误
  • 返回 2 表示执行错误:命令没有正确执行
  • 返回 -2 代表算术错误:创建命令 shell 无法处理的过大的数
当然了用户脚本可以随意指定错误码,只要保证 0 是正常执行即可
定义变量
  1. set variable_name=variable_value
复制代码
注意在变量名与变量值中,空格都是有效的!因此按照
  1. :: 将变量 a 值置为空,也是声明变量的方式
  2. set a=
  3. :: 定义的变量是 `x `, 请尽量避免这样的写法!
  4. set x = 123
  5. :: 输出变量
  6. echo %x %
复制代码
上述 echo %x  输出内容为  123。
注意 shell 中所有变量都是以字符串的形式存储,即使将变量的值设置为数值也是如此!另外 @, , &, |, ^ 为命令行保留字符(应该尽量避免出现在变量或者值中)。
echo 2^&3 输出内容为 2&3,echo 2^^3 输出内容为 2^3,但如果要定义变量,需要处理为
  1. set x=2^^^&3
  2. set y=2^^^^3
  3. echo %x%
  4. echo %y%
复制代码
变量范围局部性

shell 中定义的变量仅在当前 shell 以及当前 shell 启动的 shell 中有效(嵌套命令的 shell),如果想让变量仅在一个小区域可见,可以用 setlocal 和 endlocal 包起来
  1. setlocal
  2. set x=1
  3. echo %x%
  4. endlocal
  5. echo %x%
复制代码
变量替换
  1. set X="C:\Users\czp\cuzperf\cplib"
  2. set X=%X:\=/%
  3. echo %X%
复制代码
把 \ 替换成 /
参数


  • %0 脚本名
  • %1 - %9 第 1 到 9 个参数
  • %* 所有参数
  • shift 把所有参数左移, %0 被丢弃, 原 %1 变成 %0,以此类推
  • shift /n, 原 %(n+1) 变成 %n, 以此类推
  • %~1 ,以此类推
变形


  • %~a 表示去掉第 %a 中的引号(如果有的话)
  • %~dpa 表示 %a 所在的绝对路径
  • %~da 表示 %a 所在路径所在的盘
  • %~pa 表示 %a 所在绝对路径(不包括盘)
  • %~na 表示 %a 文件名(不带后缀)
  • %~xa 表示 %a 文件后缀
这些变形可以组合使用例如 %~dpa
从而可以推出

  • %~0 去掉脚本中的引号
  • %~dp0 为当前脚本所在的绝对路径
  • %~d0 当前路径所在的盘
  • %~p0 当前路径所在绝对路径(不包括盘)
  • %~n0 脚本名去掉后缀
  • %~x0 脚本后缀
  • %~nx0 脚本名
例如运行 "C:\Users\czp\Documents\WeChat Files\test.bat" 则
  1. %0      "C:\Users\czp\Documents\WeChat Files\test.bat"
  2. %~0     C:\Users\czp\Documents\WeChat Files\test.bat
  3. %~f0    C:\Users\czp\Documents\WeChat Files\test.bat
  4. %~dp0   C:\Users\czp\Documents\WeChat Files\
  5. %~d0    C:
  6. %~p0    \Users\czp\Documents\WeChat Files\
  7. %~n0    test
  8. %~x0    .bat
  9. %~s0    C:\Users\czp\DOCUME~1\WECHAT~1\test.bat
  10. %~a0    --a--------
  11. %~t0  08/29/2024 15:09
复制代码
数学表达式

使用 set /a,所有数学表达式都是针对 32 位有符号整数进行运算,取值范围为 \([-2^{31}, 2^{31} - 1]\),否则 errorlevel 非零
  1. set /a 1 + 2
  2. set /a x= 1 + 2
  3. echo %x%
复制代码
比较运算符

==, equ, neq, lss, leq, gtr, geq
比较时默认是大小写敏感的,加上 /I 或 /i 则大小写不敏感
for 循环

for 循环有多重形式,最基本的为 for iterator do (statement) 的句型

  • iterator 变量只出现在 for 循环中
  • 变量名必须在 a~z 或 A~Z 范围内, 例如 %%A, %%b
在命令行交互模式下 %%A 应该写成 %A
for 遍历值
  1. for /l %%a in (0, 2, 10) do (
  2.   echo %%a
  3. )
复制代码
输出为 0, 2, 4, 6, 8, 10(每个数字都换行)
for 遍历文件
  1. for %%a in (./*.txt ./*.md) do (
  2.   echo %%a
  3. )
复制代码
示例遍历当前路径下所有 txt 和 md 文件
for 遍历目录
  1. for /d %%a in (%SystemRoot%\*) do (
  2.   echo %%a
  3. )
复制代码
列出 %SystemRoot% 下所有目录,但不嵌套,想要嵌套可以再添加 /r 选项,但语法稍有不同: for /r [path] %%variable in (fileSet) do (statement)
例如
  1. for /r %SystemRoot% %%a in (*.txt) do (
  2.   echo %%a
  3. )
复制代码
for 循环分析文件的内容与输出
  1. for /f ["options"] %%variable in (source) do (statement)
复制代码

  • usebackq 可以用来处理名字中有空格,变量值用引号包起来的情况(十分有用!)
  • tokens 挺有趣但可不用
  • delims 指定分隔符
创建子程序与过程
  1. :: 用 : 定义 lable,然后就可以 goto 了
  2. :lable
复制代码
  1. :subfunc
  2. :: do something
  3. :: 返回到调用的地方
  4. goto :eof
复制代码
注意事项

对于复杂场景,应尽量使用延迟拓展功能:
  1. setlocal enableDelayedExpansion
  2. set LAST_PARAM=""
  3. call :deal_params -I
  4. call :deal_params "C:/24b(daynew)/Hello World"
  5. :deal_params
  6. if %LAST_PARAM% == "" (
  7. set LAST_PARAM="%~1"
  8. goto :eof
  9. )
  10. set "CUR_PARAM=%~1"
  11. if %LAST_PARAM% == "-I" (
  12. echo /I %1 >> 1.txt
  13. echo !CUR_PARAM! >> 2.txt
  14. )
  15. set LAST_PARAM=""
  16. goto :eof
复制代码
如果不使用延迟拓展功能,则会出现莫名其妙的报错
  1. set LAST_PARAM=""
  2. call :deal_params -I
  3. call :deal_params "C:/24b(daynew)/Hello World"
  4. :deal_params
  5. if %LAST_PARAM% == "" (
  6. set LAST_PARAM="%~1"
  7. goto :eof
  8. )
  9. if %LAST_PARAM% == "-I" (
  10. echo /I %1 >> 1.txt
  11. echo %~1 >> 2.txt
  12. )
  13. set LAST_PARAM=""
  14. goto :eof
复制代码
Windows 命令行长度有 8191 的限制,好在很多工具(gcc、cl、link)都支持将参数写到文件中,然后用 @%filename% 的方式读取。
Linux 命令行同样有长度限制,不过为 128K

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册