学习这个古老的技能是因为最近 2024.8.28 优化 Windows build 脚本遇到了一些不懂的内容,故而想要系统的进行学习并记录(权当技术沉淀)
参考文献:《Windows 命令行详解手册》第二版第三章
由于 Windows 命令 不区分大小写 ,故而可能出现大小写混用问题。另外命令行有些操作较为反常识,使用时还是多借助 AI 和小测例搞清楚。
回显
windows bat 脚本本质上就是一行行的在控制台上输入命令,导致此执行脚本的时候就会有一长串的输出,干扰了我们观察结果。所以通常 bat 脚本的第一行有 @echo off
来关闭回显。
这里在行首加上 @
表示关闭此行的回显。 @echo off
的意思是关闭后续回显,此行的回显也不出现。
当脚本运行出现问题时, echo on
或删掉/注释掉第一行,根据回显的输出诊断脚本出错位置。
特殊用法
:: 查看 echo 状态是开启还是关闭echo:: 显示空行echo .
注释
- 如果这行注释是提示信息,我们可以使用
rem
进行注释(在 echo on 时会回显) - 如果这行注释只是解释脚本的功能、创建时间、作者信息等可以用
@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&3
, echo 2^^3
输出内容为 2^3
,但如果要定义变量,需要处理为
set x=2^^^&3set y=2^^^^3echo %x%echo %y%
变量范围局部性
shell 中定义的变量仅在当前 shell 以及当前 shell 启动的 shell 中有效(嵌套命令的 shell),如果想让变量仅在一个小区域可见,可以用 setlocal
和 endlocal
包起来
setlocalset x=1echo %x%endlocalecho %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 + 2set /a x= 1 + 2echo %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 遍历值
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
注意事项
对于复杂场景,应尽量使用延迟拓展功能:
setlocal enableDelayedExpansionset LAST_PARAM=""call :deal_params -Icall :deal_params "C:/24b(daynew)/Hello World":deal_paramsif %LAST_PARAM% == "" (set LAST_PARAM="%~1"goto :eof)set "CUR_PARAM=%~1"if %LAST_PARAM% == "-I" (echo /I %1 >> 1.txtecho !CUR_PARAM! >> 2.txt)set LAST_PARAM=""goto :eof
如果不使用延迟拓展功能,则会出现莫名其妙的报错
set LAST_PARAM=""call :deal_params -Icall :deal_params "C:/24b(daynew)/Hello World":deal_paramsif %LAST_PARAM% == "" (set LAST_PARAM="%~1"goto :eof)if %LAST_PARAM% == "-I" (echo /I %1 >> 1.txtecho %~1 >> 2.txt)set LAST_PARAM=""goto :eof
Windows 有 8191 的限制,好在很多工具(gcc、cl、link)都支持将参数写到文件中,然后用 @%filename% 的方式读取。
Linux 命令行同样有 ,不过为 128K