Windows bat 脚本学习记录

学习这个古老的技能是因为最近 2024.8.28 优化 Windows build 脚本遇到了一些不懂的内容,故而想要系统的进行学习并记录(权当技术沉淀)

参考文献:《Windows 命令行详解手册》第二版第三章

由于 Windows 命令不区分大小写,故而可能出现大小写混用问题。另外命令行有些操作较为反常识,使用时还是多借助 AI 和小测例搞清楚。

回显

windows bat 脚本本质上就是一行行的在控制台上输入命令,导致此执行脚本的时候就会有一长串的输出,干扰了我们观察结果。所以通常 bat 脚本的第一行有 @echo off 来关闭回显。

这里在行首加上 @ 表示关闭此行的回显。@echo off 的意思是关闭后续回显,此行的回显也不出现。

当脚本运行出现问题时,echo on 或删掉/注释掉第一行,根据回显的输出诊断脚本出错位置。

特殊用法

:: 查看 echo 状态是开启还是关闭
echo
:: 显示空行
echo .

注释

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

标题

TITLE 命令即可,在执行长耗时的任务时,通过切换 TITLE 了解当前处于什么阶段。

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 是正常执行即可

定义变量

set variable_name=variable_value

注意在变量名与变量值中,空格都是有效的!因此按照

:: 将变量 a 值置为空,也是声明变量的方式
set a=

:: 定义的变量是 `x `, 请尽量避免这样的写法!
set x = 123
:: 输出变量
echo %x %

上述 echo %x 输出内容为 123

注意 shell 中所有变量都是以字符串的形式存储,即使将变量的值设置为数值也是如此!另外 @, <, >, &, |, ^ 为命令行保留字符(应该尽量避免出现在变量或者值中)。

echo 2^&3 输出内容为 2&3echo 2^^3 输出内容为 2^3,但如果要定义变量,需要处理为

set x=2^^^&3
set y=2^^^^3
echo %x%
echo %y%

变量范围局部性

shell 中定义的变量仅在当前 shell 以及当前 shell 启动的 shell 中有效(嵌套命令的 shell),如果想让变量仅在一个小区域可见,可以用 setlocalendlocal 包起来

setlocal
set x=1
echo %x%
endlocal
echo %x%

变量替换

set X="C:\Users\czp\cuzperf\cplib"
set X=%X:\=/%
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"

%0      "C:\Users\czp\Documents\WeChat Files\test.bat"
%~0     C:\Users\czp\Documents\WeChat Files\test.bat
%~f0    C:\Users\czp\Documents\WeChat Files\test.bat
%~dp0   C:\Users\czp\Documents\WeChat Files\
%~d0    C:
%~p0    \Users\czp\Documents\WeChat Files\
%~n0    test
%~x0    .bat
%~s0    C:\Users\czp\DOCUME~1\WECHAT~1\test.bat
%~a0    --a--------
%~t0  08/29/2024 15:09

数学表达式

使用 set /a,所有数学表达式都是针对 32 位有符号整数进行运算,取值范围为 $[-2^{31}, 2^{31} - 1]$,否则 errorlevel 非零

set /a 1 + 2
set /a x= 1 + 2
echo %x%

比较运算符

==, equ, neq, lss, leq, gtr, geq

比较时默认是大小写敏感的,加上 /I/i 则大小写不敏感

for 循环

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

  • iterator 变量只出现在 for 循环中
  • 变量名必须在 a~zA~Z 范围内, 例如 %%A, %%b

在命令行交互模式下 %%A 应该写成 %A

for 遍历值

for /l %%a in (0, 2, 10) do (
  echo %%a
)

输出为 0, 2, 4, 6, 8, 10(每个数字都换行)

for 遍历文件

for %%a in (./*.txt ./*.md) do (
  echo %%a
)

示例遍历当前路径下所有 txt 和 md 文件

for 遍历目录

for /d %%a in (%SystemRoot%\*) do (
  echo %%a
)

列出 %SystemRoot% 下所有目录,但不嵌套,想要嵌套可以再添加 /r 选项,但语法稍有不同: for /r [path] %%variable in (fileSet) do (statement)

例如

for /r %SystemRoot% %%a in (*.txt) do (
  echo %%a
)

for 循环分析文件的内容与输出

for /f ["options"] %%variable in (source) do (statement)
  • usebackq 可以用来处理名字中有空格,变量值用引号包起来的情况(十分有用!)
  • tokens 挺有趣但可不用
  • delims 指定分隔符

创建子程序与过程

:: 用 : 定义 lable,然后就可以 goto 了
:lable
:subfunc
:: do something

:: 返回到调用的地方
goto :eof

注意事项

Windows 命令行长度有 8191 的限制,好在很多工具(gcc、cl、link)都支持将参数写到文件中,然后用 @%filename% 的方式读取。

Linux 命令行同样有长度限制,不过为 128K