变量与常量的批处理文件比较失败 [英] Batch file comparison of variable with constant fails

查看:28
本文介绍了变量与常量的批处理文件比较失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写一段简单的代码来获得一些漂亮的格式化时间戳".将时间放入我的两个变量 StartEnd 工作正常.我也可以将其打印为 0:0:0.如果小于 10,我希望有一个前导零,但显然我收到一条错误消息,提示未找到参数 10 或输入错误".我发现这似乎是要比较的变量,但我没能解决这个问题.有什么想法吗?

@ECHO 关闭REM 时间计算FOR/F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Second/Format:table ^| findstr/r "."') DO (设置天=%%A设置小时=%%B设置分钟=%%C设置秒=%%D)set/a Start=%Day%*8640000+%Hour%*360000+%Minute%*6000+%Second%*100@回声开平 8.8.8.8 -n 11@ECHO 关闭FOR/F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Second/Format:table ^| findstr/r "."') DO (设置天=%%A设置小时=%%B设置分钟=%%C设置秒=%%D)set/a End=%Day%*8640000+%Hour%*360000+%Minute%*6000+%Second%*100set/a Diff=%End%-%Start%设置/a Diff=(%Diff%)/100设置/a DiffSec=%Diff%%%60set/a Diff=(%Diff%-%Diff%%%%60)/60设置/a DiffMin=%Diff%%%60set/a Diff=(%Diff%-%Diff%%%%60)/60设置/a DiffHrs=%Diff%ECHO Laufzeit Auftraege loeschen: %DiffHrs%:%DiffMin%:%DiffSec%:: 带前导零的格式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 (设置 DiffMin=0%DiffMin%)else [%DiffMin%] LSS 1 (设置 DiffMin=00)::如果 %DiffHrs% LSS 10(设置 DiffHrs=0%DiffHrs%)else [%DiffHrs%] LSS 1(设置 DiffHrs=00)ECHO Laufzeit Auftraege loeschen: %DiffHrs%:%DiffMin%:%DiffSec%

解决方案

1.调试批处理文件

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

默认情况下,Windows 命令解释器会在解析和预处理使用 <在执行命令行/块之前,代码>%variable%(立即扩展)已被环境变量的当前值替换.

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

在命令提示符窗口中运行 echo/? 的帮助输出中非常简要地解释了 ECHO 行为.

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

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

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

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

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

为了调试较大的批处理文件,在批处理文件顶部使用临时添加的 goto 跳转到某个块和 goto :EOF 可能会有所帮助块调试后退出批处理.

顺便说一句::: 是一个无效标签,通常用于批处理文件中的注释,因为在执行批处理文件时永远不会显示标签行.但是在 FOR 循环的命令块中不能使用标签,因为 Windows 命令解释器无法正确解释命令块内带有标签的 FOR 循环.出于这个原因,最好使用命令 REM(备注)进行注释,因为该命令是为批处理文件中的注释而设计的,并且真正适用于批处理文件中的任何地方.

另请参阅如何调试 .BAT 脚本?

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"

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

如果 %DiffSec% LSS 10 (ECHO 少于 10") 否则如果 %DiffSec% LSS 1 (回声少1")

语法现在是正确的.

但第二个条件没有意义,因为 JosefZ 在他的评论中已经提到.如果 DiffSec 的值为 10 或更大导致在 ELSE 分支中执行 IF 命令,则此条件也绝对不会为真.所以更有意义:

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

或者替代

如果 %DiffSec% LSS 1 (回声少 1) 否则如果 %DiffSec% LSS 10 (回声小于 10)

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

上的答案

3.为数字添加前导零 <10

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

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

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

在不使用字符串替换真正测试值的情况下,将前导零添加到小于 10 的数字是很容易的.

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

设置DiffSec=0%DiffSec%";

接下来将最后一个 X 字符(如 2 表示两位数)从环境变量的当前值分配给环境变量.

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

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

这两行的结果是 DiffSec 的值为 099 在这两行之后总是一个两位数的数字0099.

4.算术表达式的解析

set/a 之后的字符串的算术表达式被 Windows 命令解释器解释为与其他字符串完全不同.

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

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

进一步的十进制、八进制和十六进制整数在算术表达式中被解释为整数.

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

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

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

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

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

另请参阅 为什么在命令行上使用set var = text"后没有带有echo %var%"的字符串输出? 有关如何仅使用 setset/P(提示)或 set/A(算术表达式).

5.固定和优化代码

有问题的代码可以针对此代码进行修复和优化:

@echo off远程时间计算对于/F skip=1 tokens=1-4";%%A in ('%SystemRoot%System32wbemwmic.exe PATH Win32_LocalTime GET Day^,Hour^,Minute^,Second') 做 (设置天=%%A设置小时=%%B设置分钟=%%C设置秒=%%D)set/A TimeStart=天 * 86400 + 小时 * 3600 + 分钟 *60 + 秒@回声%SystemRoot%System32ping.exe 8.8.8.8 -n 11@回声关闭对于/F skip=1 tokens=1-4";%%A in ('%SystemRoot%System32wbemwmic.exe PATH Win32_LocalTime GET Day^,Hour^,Minute^,Second') 做 (设置天=%%A设置小时=%%B设置分钟=%%C设置秒=%%D)set/A TimeEnd=天 * 86400 + 小时 * 3600 + 分钟 *60 + 秒设置/A TimeDiff=TimeEnd - TimeStartset/A DiffSec=TimeDiff %% 60set/A TimeDiff=(TimeDiff - DiffSec)/60set/A DiffMin= TimeDiff %% 60设置/A DiffHrs=(TimeDiff - DiffMin)/60设置DiffSec=0%DiffSec%";设置DiffSec=%DiffSec:~-2%";设置DiffMin=0%DiffMin%";设置DiffMin=%DiffMin:~-2%";设置DiffHrs=0%DiffHrs%";设置DiffHrs=%DiffHrs:~-2%";echo 删除订单所需时间:%DiffHrs%:%DiffMin%:%DiffSec%

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

  • echo/?
  • for/?
  • ping/?
  • rem/?
  • set/?
  • wmic/?
  • wmic 路径/?

I want to write a simple piece of code to get some nice formated "time stamp". Getting the time into my two variables Start and End works fine. Also I can print it as 0:0:0. I want to have a leading zero if its less than 10, but apparently I get an error saying 'parameter 10 is not found or misstyped'. I found out that this seems to be the variable to be compared but I failed to fix that. Any ideas?

@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. Debugging a batch file

For debugging a batch file to find syntax errors in code it is recommended to run the batch file from within a command prompt window after having each echo off modified to echo ON or removed from batch file or commented out with command REM.

By default Windows command interpreter outputs each command line or an entire command block starting with ( and ending with matching ) after parsing and preprocessing on which environment variables referenced with %variable% (immediate expansion) are already replaced by current value of the environment variable before executing the command line / block.

This default behavior is turned off with @echo off at top of the batch file whereby @ at beginning of the command line disables also output of this first command line. That is of course welcome when the development of the batch file is finished and the batch file is working fine. But for debugging a batch file not working as expected it is better to see also the command lines really executed by command interpreter to find out where the batch file execution is exited unexpected because of an error.

The ECHO behavior is explained very briefly in help output on running echo /? from within a command prompt window.

Opening a command prompt window results in starting cmd.exe implicitly with option /K to keep the command process running and the console window open after execution of a batch file or application finished.

An exception is when the batch file contains the command exit without parameter /B because in this case the current command process is always exited independent on calling hierarchy. exit /B is equal goto :EOF and should be used instead of just exit, except there is a really good reason for using just exit. exit /B and goto :EOF require both command extensions as enabled by default on Windows.

Double clicking on a batch file results in starting cmd.exe with option /C to close command process and its console window automatically when execution of the application or batch file is finished independent on the reason why the execution of a batch file terminated. This behavior with closing automatically the console window is not good for debugging a batch file because the error message can't be seen when batch file execution terminated because of a syntax error.

For more details about options of Windows command interpreter run in a command prompt window the command: cmd /?

How to exit intentionally the execution of a batch file using goto :EOF (colon is here important by way of exception) or exit /B (just an internal alias for goto :EOF) is explained in help of those two commands displayed by running goto /? and exit /? in a command prompt window.

For debugging a larger batch file it could be helpful to use a temporarily added goto at top of the batch file to jump to a certain block and a goto :EOF to exit batch processing after the block to debug.

By the way: :: is an invalid label often used for comments in batch files as label lines are never displayed on execution of a batch file. But in the command block of a FOR loop labels can't be used because Windows command interpreter fails to interpret right a FOR loop with labels inside the command block. For that reason it is better to use command REM (remark) for comments as this command is designed for comments in batch files and really work anywhere in a batch file.

See also How can I debug a .BAT script?

2. Error in batch file

On running the batch file posted in question with both @ECHO OFF commented out by replacing it with rem @echo off (run a replace in text editor) from within a command prompt window it can be seen easily on which line the error occurs:

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

If current value of environment variable DiffSec is not lower than 10 then the ELSE branch is executed by Windows command interpreter which starts with the number like 10.

Windows command interpreter can't find an application with that name in current directory or any directory specified in semicolon separated directory list of environment variable PATH having a file extension specified in semicolon separated file extension list of environment variable PATHEXT.

The mistake is here obviously the missing IF command for the next comparison. So the correct code would be

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

This would be easier to read on writing the condition on multiple lines:

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

The syntax is now correct.

But the second condition does not make sense as JosefZ mentioned already in his comment. If value of DiffSec is 10 or greater resulting in execution of the IF command in ELSE branch, this condition is definitely also never true. So more sense would make:

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

Or alternatively

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

For more information about valid IF ELSE conditions in batch files see for example the answers on

3. Add leading zero for numbers < 10

Environment variables are always of type string. For arithmetic expressions the string value of an environment variable is converted to a signed 32-bit integer if possible at all and the result of the arithmetic expression is converted back from signed 32-bit integer to string.

Also an IF condition like if %DiffSec% LSS 10 being expanded before execution for example to if 5 LSS 10 results in converting 5 (0x35) from string to integer and 10 (0x31 0x30) also from string to integer for comparing the two numbers as integers.

So it would be a bit faster to avoid such a number comparison if that is possible.

It is quite easy to add a leading zero to a number lower than 10 without really testing on value by using string substitution.

First the current value of the environment variable is prepended with one (for a two digits number) or more 0 (for 3, 4 or even more digits).

set "DiffSec=0%DiffSec%"

Next the last X characters like 2 for a two digit number are assigned from current value of environment variable to the environment variable.

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

String substitution is explained in help of command SET output by running in a command prompt window set /?.

The result of the two lines is that DiffSec has for values 0 to 99 after those two lines always a two digit number in range 00 to 99.

4. Parsing of an arithmetic expression

An arithmetic expression which is the string after set /a is interpreted by Windows command interpreter completely different than other strings.

Spaces and tabs are word separators, but have no further special meaning. Therefore it is advisable to use spaces to make the arithmetic expression better readable.

Then there are a lot of operators which are listed in help of command SET displayed on running in a command prompt window set /?.

Further decimal, octal and hexadecimal integers are interpreted in an arithmetic expression as integers.

And last every other string is interpreted as name of an environment variable of which current value is converted from string to integer.

For that reason it is not advisable to use immediate or delayed expansion in an arithmetic expression.

Referencing value of an environment variable with %variable% within an arithmetic expression is not good when used within a command block on which the current value of the environment variable replaces the variable reference already on parsing the entire command block before executing the first command.

Referencing value of an environment variable with !variable! within an arithmetic expression is also not good because it requires enabling delayed expansion which results in handling exclamation marks in strings not anymore as literal characters.

So best is always simply writing the variable names in an arithmetic expression without surrounding percent signs or exclamation marks if that is possible because of variable name does not contain a space character and starting with a character which can't be interpreted as integer character by Windows command interpreter.

See also answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? for details on how to assign a value to an environment variable using just set or set /P (prompt) or set /A (arithmetic expression).

5. Fixed and optimized code

The code in question can be fixed and optimized to this code:

@echo off
rem Time Calculation
for /F "skip=1 tokens=1-4" %%A in ('%SystemRoot%System32wbemwmic.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%System32ping.exe 8.8.8.8 -n 11
@echo off

for /F "skip=1 tokens=1-4" %%A in ('%SystemRoot%System32wbemwmic.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%

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

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

这篇关于变量与常量的批处理文件比较失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆