本帖最后由 CrLf 于 2012-8-14 03:33 编辑
常用调试手段简介:
1、通过 call 或 cmd /k 运行脚本,一般可以在脚本头部加上一行 %1 @cmd /k %0 : 来观察错误退出时的提示。
错例:- if !date:~,4!==2012 echo 末日年
- rem 一闪而过
复制代码 调试:- %1 cmd /k %0 :
- if !date:~,4!==2012 echo 末日年
- rem 可观察到错误信息为“此时不应有 4!==2012。”
- rem 分析错误原因,if 解析语法时 !date:~,4! 尚未被解释,符号 , 错被当成 if 的分隔符产生语法错误
复制代码 修正:- if %date:~,4%==2012 echo 末日年
- rem 使用在解析语法之前被解释的 %str% 形式的变量
- if !date:~^,4!==2012 echo 末日年
- rem 将 if 中的默认分隔符转义
- if "!date:~,4!"=="2012" echo 末日年
- rem 用双引号转义也可
复制代码 2、替换 @echo off 和 >nul 为空(如果有 2>nul 的话要先去除 2>nul),运行观察提示信息。
如果要求纠错后能方便地还原,可以将 echo off 替换为 echo on,将所有 >nul 暂时替换为 >"con"。
错例:- @echo off
- set n=Test123
- echo %n%>1.txt
- rem 为什么文件为空?
复制代码 调试:- echo on
- set n=
- Test123
- echo %n%>1.txt
- pause
- rem 可以看到预处理的结果是 echo Test123 3>1.txt
- rem 很明显,这里的 3 被理解成重定向的句柄号了,而 echo Test123 运行时的句柄3 压根没有输出,定向到文件自然为空。
- rem 当脚本很长时,可以只在关键代码前加上 echo on 进行目的性明确的分析,而避免过多的干扰。
复制代码 修正:- @echo off&setlocal enabledelayedexpansion
- set n=Test123
- echo !n!>1.txt
- rem 变量延迟的特性是等语法解析进行完后,再对 !n! 的变量形式进行解释
- @echo off
- set n=Test123
- echo>1.txt %n%
- rem 避免重定向符号与数字接触
- @echo off
- set n=Test123
- >1.txt echo %n%
- rem 同理
复制代码 3、下断点,常用 pause(建议写成 pause<con>con 增强抗干扰能力),这是最常见的手段,往往配合其他命令查看断点位置的进度和环境。有时也会视情况改用 cmd 进行更深入的调试。
错例:- type 1.txt>1.txt
- rem 为何 1.txt 为空呢?
复制代码 调试:- (pause>con
- type 1.txt)>1.txt
- rem 逐步观察 1.txt 的变化,发现执行到暂停处 1.txt 为空了
- rem 分析可知,重定向发生于语法解释之后、命令执行之前,所以 (...)>1.txt 这个重定向在 type 读取 1.txt 之前就先把 1.txt 清空了,type 当然得不到输入
复制代码 修正:- type 1.txt>tmp.txt
- move /y tmp.txt 1.txt
- rem 使用临时文件,再覆盖回来
复制代码 4、在关键命令前加个 echo 观察其真实的输入参数,如果确认命令运行了却看不到输出,可能是 echo 的输出被重定向,可在命令后加个 >con 再试。注意要对原命令中的特殊字符 (、)、&、|、<、> 等进行转义,所以经常是写成 echo "原命令"
错例:- for /f "delims=" %%a in ('dir /b /od *.txt') do if defined old set old=%%a
- echo 最老的文件是:
- %old%
- rem 我希望获得修改日期最早的文件名称(即 dir /b /od *.txt 的第一条输出),可为什么取不到?
复制代码 调试:- setlocal enabledelayedexpansion
- for /f "delims=" %%a in ('dir /b /od *.txt') do echo if defined old[!old!] set old=%%a
- rem 因为 if defined 是直接读 old 变量的,所以我们要把这里的变量名加上 ! 成看看 old 变量的值究竟是什么
- echo 最老的文件是:
- %old%
- rem 观察到 !old! 一直为空
- rem 原来是 if defined old 这里逻辑有误,漏了个 not
复制代码 修正:- setlocal enabledelayedexpansion
- for /f "delims=" %%a in ('dir /b /od *.txt') do echo if not defined old set old=%%a
- echo 最老的文件是:
- %old%
复制代码 5、使用 "if errorlevel" 或逻辑连接符来判断命令的结果
错例:- for /l %%a in (1 1 100) do setlocal
- rem 提示“已经达到最大的 setlocal 递归层。”
- rem 那如何知道何时出错的呢?
复制代码 调试:
- for /l %%a in (1 1 100) do setlocal||echo 在 %%a 层时出错&&pause
- rem 这里的逻辑关系是:当 setlocal 失败或未执行时执行 echo,当 echo 成功时执行 pause
- rem 显示“在 33 层时出错”
- rem 原来 setlocal 最大递归层只有 32,第33层 setlocal 将出错
复制代码 修正:- for /l %%a in (1 1 100) do setlocal&endlocal
- rem 及时 endlocal 销毁 setlocal 创建的局部变量表
- for /l %%a in (1 1 100) do call :test
- pause&exit
- :test
- setlocal
- rem 每一次 call 都可独享 32 次的 setlocal(甚至可以突破 cmd 的64 兆内存上限),所以当 setlocal 实在不够用时(尽量避免,极端情况才考虑),可以考虑用 call 弥补
复制代码 6、用 debug 精确查看命令的输出内容,以便发现一些隐蔽的错误
错例:- for /f "skip=1 delims=" %%a in ('wmic logicaldisk get name') do echo 盘符 %%a
- rem 单独运行 wmic 似乎很正常,可为何用 for 输出总是多出一行?
复制代码 调试:- wmic logicaldisk get name|more>1.txt
- rem wmic 的输出是 uniocode 编码的,所以建议用 more 命令转换为 ansi
- debug 1.txt
- rem 注意 debug 不支持长路径,路径中若含有超过 8 字节宽度的文件夹名或文件名建议使用路径短名
- rem 以下为 debug 中的输入及输出
- -d100
- 0B4E:0100 4E 61 6D 65 20 20 0D 0D-0A 43 3A 20 20 20 20 0D Name ...C: .
- 0B4E:0110 0D 0A 44 3A 20 20 20 20-0D 0D 0A 45 3A 20 20 20 ..D: ...E:
- 0B4E:0120 20 0D 0D 0A 46 3A 20 20-20 20 0D 0D 0A 47 3A 20 ...F: ...G:
- 0B4E:0130 20 20 20 0D 0D 0A 48 3A-20 20 20 20 0D 0D 0A 49 ...H: ...I
- 0B4E:0140 3A 20 20 20 20 0D 0D 0A-4A 3A 20 20 20 20 0D 0D : ...J: ..
- 0B4E:0150 0A 4B 3A 20 20 20 20 0D-0D 0A 4C 3A 20 20 20 20 .K: ...L:
- 0B4E:0160 0D 0D 0A 4D 3A 20 20 20-20 0D 0D 0A 0D 0D 0A 0D ...M: .......
- 0B4E:0170 0A 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
- rem 观察到 wmic 的输出都以 0D 结尾,而 wmic 的输出中多了一行只有 0D 的内容,因此引发了 for /f 的一次多余的循环
复制代码 修正:- for /f "skip=1 delims=" %%a in ('wmic logicaldisk get name') do if exist %%a\nul echo 盘符 %%a
- rem 判断盘符是否存在
- for /f "skip=1 delims=" %%a in ('wmic logicaldisk get name') do if %%a geq a: echo 盘符 %%a
- rem 判断结果是否大等于 a:
- for /f "skip=1 delims=" %%a in ('wmic logicaldisk get name') do call :e %%a
- pause&exit
- :e
- echo 盘符 %1
- rem 用 call 排除 0D
复制代码
|