变量与常量的批处理文件比较失败

Posted

技术标签:

【中文标题】变量与常量的批处理文件比较失败【英文标题】:Batch file comparison of variable with constant fails 【发布时间】:2017-07-15 02:36:53 【问题描述】:

我想编写一段简单的代码来获得一些格式良好的“时间戳”。 将时间花在我的两个变量 StartEnd 上效果很好。我也可以将其打印为 0:0:0。如果小于 10,我希望有一个前导零,但显然我收到一条错误消息,提示“未找到参数 10 或输入错误”。 我发现这似乎是要比较的变量,但我没有解决这个问题。有什么想法吗?

@ECHO OFF
REM Time Calculation
FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Second /Format:table ^| findstr /r "."') DO (
 set Day=%%A
 set Hour=%%B
 set Minute=%%C
 set Second=%%D
)
set /a Start=%Day%*8640000+%Hour%*360000+%Minute%*6000+%Second%*100
@ECHO ON
ping 8.8.8.8 -n 11
@ECHO OFF
FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Second /Format:table ^| findstr /r "."') DO (
 set Day=%%A
 set Hour=%%B
 set Minute=%%C
 set Second=%%D
)
set /a End=%Day%*8640000+%Hour%*360000+%Minute%*6000+%Second%*100
set /a Diff=%End%-%Start%
set /a Diff=(%Diff%)/100
set /a DiffSec=%Diff%%%60
set /a Diff=(%Diff%-%Diff%%%60)/60
set /a DiffMin=%Diff%%%60
set /a Diff=(%Diff%-%Diff%%%60)/60
set /a DiffHrs=%Diff%

ECHO Laufzeit Auftraege loeschen: %DiffHrs%:%DiffMin%:%DiffSec%

:: format with leading zeroes
if %DiffSec% LSS 10 (ECHO "LESS 10")else %DiffSec% LSS 1 (ECHO "LESS 1")
::if %DiffSec% LSS 10 (set DiffSec=0%DiffSec%)else [%DiffSec%] LSS 1 (set DiffSec=00)
::if %DiffMin% LSS 10 (set DiffMin=0%DiffMin%)else [%DiffMin%] LSS 1 (set DiffMin=00)
::if %DiffHrs% LSS 10 (set DiffHrs=0%DiffHrs%)else [%DiffHrs%] LSS 1 (set DiffHrs=00)

ECHO Laufzeit Auftraege loeschen: %DiffHrs%:%DiffMin%:%DiffSec%

【问题讨论】:

【参考方案1】:
if %DiffSec% LSS 10 (ECHO "LESS 10")else IF %DiffSec% LSS 1 (ECHO "LESS 1")

else 后面需要一个if

【讨论】:

if %DiffSec% LSS 1 (ECHO "LESS 1") else IF %DiffSec% LSS 10 (ECHO "LESS 10")。否则,else 永远不会对小于 1 的任何值生效。【参考方案2】:

1。调试批处理文件

为了调试批处理文件以查找代码中的语法错误,建议在将每个 echo off 修改为 echo ON 或从批处理文件中删除或使用命令注释掉之后从命令提示符窗口中运行批处理文件 REM。

默认情况下,Windows 命令解释器输出每个命令行或整个命令块,以 ( 开头并以匹配的 ) 结尾,在解析和预处理以 %variable%(立即扩展)引用的环境变量已被替换为执行命令行/块之前环境变量的当前值。

此默认行为在批处理文件顶部使用@echo off 关闭,因此命令行开头的@ 也会禁用第一个命令行的输出。当批处理文件的开发完成并且批处理文件工作正常时,这当然是受欢迎的。但是为了调试一个没有按预期工作的批处理文件,最好也查看命令解释器真正执行的命令行,以找出批处理文件执行因错误而意外退出的位置。

ECHO 行为在有关从命令提示符窗口中运行 echo /? 的帮助输出中得到了非常简要的说明。

打开命令提示符窗口会导致使用选项/K 隐式启动cmd.exe,以保持命令进程运行并在批处理文件或应用程序执行完成后打开控制台窗口。

当批处理文件包含不带参数/B 的命令exit 时例外,因为在这种情况下,当前命令进程总是独立于调用层次结构而退出。 exit /B 等于 goto :EOF,应该使用而不是仅仅使用 exit,除非有一个非常好的理由只使用 exitexit /Bgoto :EOF 需要在 Windows 上默认启用这两个命令扩展。

双击批处理文件会导致启动cmd.exe 并带有选项/C关闭 命令进程及其控制台窗口在应用程序或批处理文件执行完成时自动完成,与原因无关为什么批处理文件的执行终止。这种自动关闭控制台窗口的行为不利于调试批处理文件,因为当批处理文件执行因语法错误而终止时,无法看到错误消息。

有关 Windows 命令解释器选项的更多详细信息,请在命令提示符窗口中运行命令:cmd /?

如何使用goto :EOF(冒号在这里作为例外很重要)或exit /B(只是goto :EOF的内部别名)有意退出批处理文件的执行,在这两个命令的帮助下进行了说明通过在命令提示符窗口中运行 goto /?exit /?

对于调试较大的批处理文件,在批处理文件顶部使用临时添加的goto 以跳转到某个块并在要调试的块后使用goto :EOF 退出批处理可能会有所帮助。

顺便说一句::: 是一个无效标签,通常用于批处理文件中的 cmets,因为在执行批处理文件时从不显示标签行。但是在 FOR 循环的命令块中不能使用标签,因为 Windows 命令解释器无法正确解释命令块内带有标签的 FOR 循环。出于这个原因,最好对 cme​​ts 使用命令 REM(备注),因为该命令是为批处理文件中的 cmets 设计的,并且确实适用于批处理文件中的任何位置。

另见How can I debug a .BAT script?

2。批处理文件出错

在运行带有 @ECHO OFF 的有问题的批处理文件时,通过在命令提示符窗口中将其替换为 rem @echo off(在文本编辑器中运行替换)将其注释掉,可以很容易地看到错误发生在哪一行:

if %DiffSec% LSS 10 (ECHO "LESS 10")else %DiffSec% LSS 1 (ECHO "LESS 1")

如果环境变量DiffSec的当前值不低于10,则ELSE分支由Windows命令解释器执行,它以10之类的数字开头。

Windows 命令解释器在当前目录或环境变量PATH 的分号分隔目录列表中指定的任何目录中找不到具有该名称的应用程序,其文件扩展名在环境变量PATHEXT 的分号分隔文件扩展名列表中指定.

这里的错误显然是缺少用于下一个比较的 IF 命令。所以正确的代码是

if %DiffSec% LSS 10 (ECHO "LESS 10") else if %DiffSec% LSS 1 ECHO "LESS 1"

在多行上写条件会更容易阅读:

if %DiffSec% LSS 10 (
    ECHO "LESS 10"
) else if %DiffSec% LSS 1 (
    ECHO "LESS 1"
)

现在语法正确。

但第二个条件没有意义,正如JosefZ 在他的评论中已经提到的那样。如果DiffSec 的值是10 或更大,导致在ELSE 分支中执行IF 命令,那么这个条件肯定也不成立。所以更有意义:

if %DiffSec% LSS 1 (ECHO LESS 1) else if %DiffSec% LSS 10 ECHO LESS 10

或者

if %DiffSec% LSS 1 (
    ECHO LESS 1
) else if %DiffSec% LSS 10 (
    ECHO LESS 10
)

有关批处理文件中有效 IF ELSE 条件的更多信息,请参阅例如关于

的答案 IF ELSE syntax error within batch file? batch scripting - if exist ./sdcard/file.any using adb

3。为数字 添加前导零

环境变量总是字符串类型。对于算术表达式,如果可能的话,环境变量的字符串值将被转换为有符号的 32 位整数,并且算术表达式的结果会从有符号的 32 位整数转换回字符串。

还有一个 IF 条件,例如 if %DiffSec% LSS 10 在执行之前被扩展为 if 5 LSS 10 导致将 5 (0x35) 从字符串转换为整数和 10 (0x31 0x30)从字符串到整数,用于将两个数字作为整数进行比较。

因此,如果可能的话,避免这样的数字比较会更快一些。

在小于 10 的数字中添加前导零非常容易,而无需使用字符串替换来真正测试值。

首先在环境变量的当前值前面加上一个(对于两位数字)或多个0(对于 3、4 甚至更多数字)。

set "DiffSec=0%DiffSec%"

接下来,last X 字符(例如 2 表示两位数)从环境变量的当前值分配给环境变量。

set "DiffSec=%DiffSec:~-2%"

在命令提示符窗口set /? 中运行命令SET 输出的帮助解释了字符串替换。

这两行的结果是DiffSec 在这两行之后的值099 始终是0099 范围内的两位数字。

4。算术表达式的解析

一个算术表达式,它是set /a 之后的字符串,Windows 命令解释器对它的解释与其他字符串完全不同。

空格和制表符是单词分隔符,但没有其他特殊含义。因此建议使用空格使算术表达式更易读。

然后在命令提示符窗口set /?中运行时显示的命令SET的帮助中列出了很多运算符。

更多的十进制、八进制和十六进制整数在算术表达式中被解释为整数。

最后每隔一个字符串被解释为环境变量的名称,其当前值从字符串转换为整数。

因此,不建议在算术表达式中使用立即展开或延迟展开。

在算术表达式中使用 %variable% 引用环境变量的值在执行第一个命令之前解析整个命令块之前已在环境变量的当前值替换变量引用的命令块中使用时不好命令。

在算术表达式中使用 !variable! 引用环境变量的值也不好,因为它需要启用延迟扩展,这导致将字符串中的感叹号不再作为文字字符处理。

所以最好总是在算术表达式中简单地写变量名,如果可能的话,不要将百分号或感叹号括起来,因为变量名不包含空格字符并且以不能解释为整数的字符开头Windows 命令解释器的字符。

有关如何仅使用setset /P(提示)或set /A(算术表达式)将值分配给环境变量的详细信息,另请参阅Why is no string output with 'echo %var%' after using 'set var = text' on command line? 上的回答。

5。修复优化代码

有问题的代码可以修复并优化为该代码:

@echo off
rem Time Calculation
for /F "skip=1 tokens=1-4" %%A in ('%SystemRoot%\System32\wbem\wmic.exe PATH Win32_LocalTime GET Day^,Hour^,Minute^,Second') do (
    set Day=%%A
    set Hour=%%B
    set Minute=%%C
    set Second=%%D
)
set /A TimeStart=Day * 86400 + Hour * 3600 + Minute *60 + Second

@echo on
%SystemRoot%\System32\ping.exe 8.8.8.8 -n 11
@echo off

for /F "skip=1 tokens=1-4" %%A in ('%SystemRoot%\System32\wbem\wmic.exe PATH Win32_LocalTime GET Day^,Hour^,Minute^,Second') do (
    set Day=%%A
    set Hour=%%B
    set Minute=%%C
    set Second=%%D
)
set /A TimeEnd=Day * 86400 + Hour * 3600 + Minute *60 + Second

set /A TimeDiff=TimeEnd - TimeStart
set /A DiffSec=TimeDiff %% 60
set /A TimeDiff=(TimeDiff - DiffSec) / 60
set /A DiffMin= TimeDiff %% 60
set /A DiffHrs=(TimeDiff - DiffMin) / 60

set "DiffSec=0%DiffSec%"
set "DiffSec=%DiffSec:~-2%"
set "DiffMin=0%DiffMin%"
set "DiffMin=%DiffMin:~-2%"
set "DiffHrs=0%DiffHrs%"
set "DiffHrs=%DiffHrs:~-2%"

echo Time needed for orders deletion: %DiffHrs%:%DiffMin%:%DiffSec%

要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

echo /? for /? ping /? rem /? set /? wmic /? wmic path /?

【讨论】:

刚回来,因为这篇文章有 1000 次浏览,我发现除了接受你的精彩回答,详细解释所有内容并优化代码之外,我没有感谢你 Mofi。 作为 1 的补充。调试批处理文件,也可以参考这个问题:How can I debug a .BAT script?

以上是关于变量与常量的批处理文件比较失败的主要内容,如果未能解决你的问题,请参考以下文章

消息处理

一个人开发一个产品,小程序从0到1,第6章 常量变量

C1数据类型,常量变量,输入输出,运算符,if/switch/循环,/数组,指针,/结构体,文件操作,/编译预处理,gdb,makefile,线程

C#基础之003 常量与变量

Jenkins执行批处理文件失败

批处理 命令执行结果赋值与比较