Windows 批处理文件中的安全数字比较 [英] Safe number comparison in Windows batch file

查看:14
本文介绍了Windows 批处理文件中的安全数字比较的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道在批处理文件中比较内容的相等性时,通常将双方括在引号中,例如

I know that when comparing stuff for equality in a batch file it's common to enclose both sides in quotes, like

IF "%myvar% NEQ "0" 

但是当使用大于"或小于"进行比较时,这不起作用,因为操作数将被视为带有引号的字符串.所以你可以只做

But when comparing using "greater than" or "less than", this doesn't work because the operands would then be treated as strings with quotes around them. So you can instead just do

IF %myvar% GTR 20000

需要注意的是,如果变量 %myvar% 没有被声明,就像这样做

The caveat is that if the variable %myvar% isn't declared, it would be like doing

IF GTR 20000

这是一个语法错误.

我想出了以下解决方法:

I came up with the following workaround:

IF 1%myvar% GTR 120000

如果 myvar 未定义,我希望这会导致 IF 1 GTR 120000,并且它似乎有效.

which I'm hoping would result in IF 1 GTR 120000 if myvar is undefined, and it seems to work.

这是比较数字和解释未声明变量的安全方法,还是我只是打开了一罐全新的警告?

Is this a safe way to compare numbers and accounting for undeclared variables, or did I just open up a whole new can of caveats?

推荐答案

假设批处理文件包含:

@echo off
:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range 0 to 20000.
set /P "MyVar=Enter number [0,20000]: "

正如我在 如何阻止 Windows 命令解释器在不正确的用户输入时退出批处理文件执行所解释的那样? 用户可以自由输入包括字符串在内的任何内容,这些字符串很容易因语法错误而导致批处理文件执行中断或导致执行批处理文件未针对其编写的操作.

As I explained by my answer on How to stop Windows command interpreter from quitting batch file execution on an incorrect user input? the user has the freedom to enter really anything including a string which could easily result in breaking batch file execution because of a syntax error or resulting in doing something the batch file is not written for.

如果用户只按RETURNENTER键,环境变量MyVar不会被命令SET<修改/强>.在这种情况下很容易验证环境变量 MyVar 在提示用户之前显式未定义的用户是否输入了一个字符串:

If the user hits just key RETURN or ENTER, the environment variable MyVar is not modified at all by command SET. It is easy to verify in this case with environment variable MyVar explicitly undefined before prompting the user if the user entered a string at all with:

if not defined MyVar goto PromptUser

注意:可以使用不同于 set "MyVar=" 的东西,比如 set "MyVar=1000" 来定义默认值值甚至可以在提示时输出,让用户可以按 RETURNENTER 使用默认值.

Note: It is possible to use something different than set "MyVar=" like set "MyVar=1000" to define a default value which can be even output on prompt giving the user the possibility to just hit RETURN or ENTER to use the default value.

用户可能有意或错误地输入包含一个或多个 " 的字符串.例如,按下 德语键盘2 在非数字键盘上使用 CapsLock 当前启用导致输入 ",除了 德语 (IBM) 用于 CapsLock 由软件仅对字母有效.因此,如果用户快速点击 2RETURN 或没有像许多人在键盘上打字那样看屏幕,则使用双引号字符而不是 2 被用户错误输入.

The user could enter a string with one or more " intentionally or by mistake. For example pressing on a German keyboard key 2 on non-numeric keyboard with CapsLock currently enabled results in entering ", except German (IBM) is used on which CapsLock is by software only active for the letters. So if the user hits 2 and RETURN quickly or without looking on screen as many people do on typing on keyboard, a double quote character instead of 2 was entered by mistake by the user.

On MyVar 持有一个字符串,其中包含一个或多个 " 所有 %MyVar%"%MyVar%" 环境变量引用存在问题,因为 %MyVar% 被 Windows 命令处理器替换为带有一个或多个 " 的用户输入字符串,这几乎总是导致语法错误或批处理文件做了一些它没有设计的事情.另请参阅Windows 命令解释器 (CMD.EXE) 如何解析脚本?

On MyVar holding a string with one or more " all %MyVar% or "%MyVar%" environment variable references are problematic because of %MyVar% is replaced by Windows command processor by user input string with one or more " which nearly always results in a syntax error or the batch file does something it was not designed for. See also How does the Windows Command Interpreter (CMD.EXE) parse scripts?

有两种解决方案:

  1. 启用延迟扩展并使用!MyVar!引用环境变量code> 或 "!MyVar!" 因为现在用户输入字符串在解析后不再影响 cmd.exe 执行的命令行.
  2. 如果该字符串不应包含双引号字符,则从用户输入字符串中删除 all ".
  1. Enable delayed expansion and reference the environment variable using !MyVar! or "!MyVar!" as now the user input string does not affect anymore the command line executed by cmd.exe after parsing it.
  2. Remove all " from user input string if this string should never contain a double quote character.

字符 " 在应该是 020000(十进制)范围内的数字的字符串中绝对无效.因此,两个可以使用更多行来防止 " 导致对用户输入字符串的错误处理.

Character " is definitely invalid in a string which should be a number in range 0 to 20000 (decimal). For that reason two more lines can be used to prevent wrong processing of user input string caused by ".

set "MyVar=%MyVar:"=%"
if not defined MyVar goto PromptUser

在用结果字符串替换 %MyVar:"=% 之前,Windows 命令处理器删除所有在解析这一行时已经存在的双引号.因此最终执行的命令行 set "MyVar=whatever由用户输入"在执行时是安全的.

The Windows command processor removes all doubles quotes already on parsing this line before replacing %MyVar:"=% with the resulting string. Therefore the finally executed command line set "MyVar=whatever was entered by the user" is safe on execution.

上面的例子错误地输入了 " 而不是 2 导致执行 set "MyVar=" 未定义环境变量MyVar 这就是在进一步处理用户输入之前必须再次使用之前使用的 IF 条件的原因.

The example above with a by mistake entered " instead of 2 results in execution of set "MyVar=" which undefines the environment variable MyVar which is the reason why the IF condition as used before must be used again before further processing of the user input.

用户应输入020000 范围内的正十进制 数字.因此,用户输入字符串中除 0123456789 之外的任何其他字符肯定是无效的.例如,可以使用以下命令检查任何无效字符:

The user should enter a positive decimal number in range 0 to 20000. So any other character than 0123456789 in user input string is definitely invalid. Checking for any invalid character can be done for example with:

for /F delims^=0123456789^ eol^= %%I in ("%MyVar%") do goto PromptUser

如果整个字符串仅由数字组成,则命令 FOR 不会执行 goto PromptUser.在所有其他情况下,包括以 ; 开头的字符串,在零个或多个数字之后会导致执行 goto PromptUser,因为输入字符串包含非数字字符.

The command FOR does not execute goto PromptUser if the entire string consists of just digits. In all other cases including a string starting with ; after zero or more digits results in execution of goto PromptUser because of input string contains a non-digit character.

Windows 命令处理器将带有前导 0 的数字解释为八进制数.但即使用户输入的数字以一个或多个 0 开头,也应解释为十进制数.因此,在进一步处理变量值之前,应删除前导零.

Windows command processor interprets numbers with a leading 0 as octal numbers. But the number should be interpreted as decimal number even on user input it with one or more 0 at beginning. For that reason the leading zero(s) should be removed before further processing variable value.

for /F "tokens=* delims=0" %%I in ("%MyVar%") do set "MyVar=%%I"
if not defined MyVar set "MyVar=0"

FOR 删除分配给 MyVar 的字符串开头的所有 0 并将剩余的分配给循环变量 I在环境变量 MyVar 旁边分配的字符串.

FOR removes all 0 at beginning of string assigned to MyVar and assigns to loop variable I the remaining string which is assigned next to environment variable MyVar.

FOR 在这种情况下运行 set "MyVar=%%I" 即使用户输入了 0000> 在这种特殊情况下,执行 set "MyVar=" 的结果是取消定义环境变量 MyVar.但是 0 是一个有效数字,因此 IF 条件是必要的,以在用户上使用字符串值 0 重新定义 MyVar输入的数字 0 带有一个或多个零.

FOR runs in this case set "MyVar=%%I" even on user entered 0 or 000 with the result of executing set "MyVar=" which undefines environment variable MyVar in this special case. But 0 is a valid number and therefore the IF condition is necessary to redefine MyVar with string value 0 on user entered number 0 with one or more zeros.

现在可以安全地使用带有运算符 GTR 的命令 IF 来验证用户输入的数字是否太大.

Now it is safe to use the command IF with operator GTR to validate if the user entered a too large number.

if %MyVar% GTR 20000 goto PromptUser

即使用户输入了 82378488758723872198735897 这比最大正 32 位整数值 2147483647 因为范围溢出导致使用 2147483647 执行此 IF 条件.有关详细信息,请参阅我在 IF 的奇怪结果 上的回答.

This last verification works even on user entering 82378488758723872198735897 which is larger than maximum positive 32 bit integer value 2147483647 because of the range overflow results in using 2147483647 on execution of this IF condition. See my answer on weird results with IF for details.

用于安全评估 020000 范围内仅十进制数字的用户输入数字的完整批处理文件是:

An entire batch file for safe evaluation of user input number in range 0 to 20000 for only decimal numbers is:

@echo off
set "MinValue=0"
set "MaxValue=20000"

:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "

if not defined MyVar goto PromptUser
set "MyVar=%MyVar:"=%"
if not defined MyVar goto PromptUser
for /F delims^=0123456789^ eol^= %%I in ("%MyVar%") do goto PromptUser
for /F "tokens=* delims=0" %%I in ("%MyVar%") do set "MyVar=%%I"
if not defined MyVar set "MyVar=0"
if %MyVar% GTR %MaxValue% goto PromptUser
rem if %MyVar% LSS %MinValue% goto PromptUser

rem Output value of environment variable MyVar for visual verification.
set MyVar
pause

此解决方案还使批处理文件编写器可以输出错误消息,通知用户为什么批处理文件不接受输入字符串.

This solution gives the batch file writer also the possibility to output an error message informing the user why the input string was not accepted by the batch file.

如果 MinValue 的值为 0,则不需要带有运算符 LSS 的最后一个 IF 条件,这就是原因为什么在此用例中使用命令 REM 将其注释掉.

The last IF condition with operator LSS is not needed if MinValue has value 0 which is the reason why it is commented out with command REM for this use case.

这是一种更安全的解决方案,其缺点是用户无法输入带有一个或多个前导 0 的十进制数,但通常会按用户的预期解释为十进制数.

Here is one more safe solution which has the disadvantage that the user cannot enter a decimal number with one or more leading 0 being nevertheless interpreted decimal as expected usually by users.

@echo off
set "MinValue=0"
set "MaxValue=20000"

:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "

if not defined MyVar goto PromptUser
setlocal EnableDelayedExpansion
set /A "Number=MyVar" 2>nul
if not "!Number!" == "!MyVar!" endlocal & goto PromptUser
endlocal
if %MyVar% GTR %MaxValue% goto PromptUser
if %MyVar% LSS %MinValue% goto PromptUser

rem Output value of environment variable MyVar for visual verification.
set MyVar
pause

此解决方案使用延迟环境变量扩展,如上述第 2 点的第一个选项.

This solution uses delayed environment variable expansion as written as first option on point 2 above.

算术表达式用于将用户输入字符串转换为有符号的 32 位整数,将字符串解释为十进制、八进制或十六进制数,并返回分配给环境变量 Number 的字符串,其中十进制Windows 命令处理器使用数字系统.由于用户字符串无效而导致的算术表达式计算错误输出被重定向到设备 NUL 以抑制它.

An arithmetic expression is used to convert the user input string to a signed 32 bit integer interpreting the string as decimal, octal or hexadecimal number and back to a string assigned to environment variable Number on which decimal numeral system is used by Windows command processor. An error output on evaluation of the arithmetic expression because of an invalid user string is redirected to device NUL to suppress it.

如果算术表达式创建的数字字符串与用户输入的字符串不同,则使用延迟扩展验证下一步.此 IF 条件适用于无效的用户输入,包括具有由 cmd.exe 解释为八进制的前导零的数字或输入的十六进制数字,如 0x14 或 <代码>0xe3.

Next is verified with using delayed expansion if the number string created by the arithmetic expression is not identical to the string entered by the user. This IF condition is true on invalid user input including number having leading zeros interpreted octal by cmd.exe or a number entered hexadecimal like 0x14 or 0xe3.

在传递字符串比较时,使用运算符 GTRMyVar 的值与 200000 进行比较是安全的code> 和 LSS.

On passing the string comparison it is safe to compare value of MyVar with 20000 and 0 using the operators GTR and LSS.

请阅读此答案,了解有关SETLOCALENDLOCAL<命令的详细信息/strong> 因为运行 setlocal EnableDelayedExpansionendlocal 不仅仅是启用和禁用延迟环境变量扩展.

Please read this answer for details about the commands SETLOCAL and ENDLOCAL because there is much more done on running setlocal EnableDelayedExpansion and endlocal than just enabling and disabling delayed environment variable expansion.

如果值 0 超出有效范围,则还有一种使用较少命令行的解决方案,即用户输入的数字必须大于 0.

There is one more solution using less command lines if the value 0 is out of valid range, i.e. the number to enter by the user must be greater 0.

@echo off
set "MinValue=1"
set "MaxValue=20000"

:PromptUser
rem Undefine environment variable MyVar in case of being already defined by chance.
set "MyVar="
rem Prompt user for a positive number in range %MinValue% to %MaxValue%.
set /P "MyVar=Enter number [%MinValue%,%MaxValue%]: "
set /A MyVar+=0
if %MyVar% GTR %MaxValue% goto PromptUser
if %MyVar% LSS %MinValue% goto PromptUser

rem Output value of environment variable MyVar for visual verification.
set MyVar
pause

此代码使用 set/A MyVar+=0 将用户输入的字符串转换为 32 位有符号整数值并返回为 aschipfl他的评论.

This code uses set /A MyVar+=0 to convert the user entered string to a 32-bit signed integer value and back to a string as suggested by aschipfl in his comment above.

如果用户根本没有输入任何字符串,MyVar 的值是 0 命令行后的算术表达式.如果用户输入字符串的第一个字符不是这些字符 -+0123456789 中的一个,例如 "/(.

The value of MyVar is 0 after command line with the arithmetic expression if the user did not input any string at all. It is also 0 if the user input string has as first character not one of these characters -+0123456789 like " or / or (.

一个用户输入的字符串开始以一个数字,或-+并且下一个字符是一个数字,被转换成一个整数值并返回一个字符串值.输入的字符串可以是十进制数或八进制数或十六进制数.请查看我对 Windows 批处理文件中与 NEQ、LSS、GTR 等等价的符号 的回答,其中解释了详细说明 Windows 命令处理器如何将字符串转换为整数值.

A user input string starting with a digit, or - or + and next character is a digit, is converted to an integer value and back to a string value. The entered string can be a decimal number or an octal number or a hexadecimal number. Please take a look on my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files which explains in detail how Windows command processor converts a string to an integer value.

此代码的缺点是错误输入字符串如 7"( 而不是 728 导致按住 Shift 按下此代码未检测到德语键盘上的 2( 键.MyVar 在用户输入时具有值 7错误地 7"(.Windows 命令处理器仅将十进制、十六进制或八进制数的第一个无效字符前的字符解释为整数值,并忽略字符串的其余部分.

The disadvantage of this code is that a by mistake input string like 7"( instead of 728 caused by holding Shift on pressing the keys 2 and ( on a German keyboard is not detected by this code. MyVar has value 7 on user enters by mistake 7"(. Windows command processor interprets just the characters up to first not valid character for a decimal, hexadecimal or octal number as integer value and ignores the rest of the string.

使用此代码的批处理文件对于批处理文件处理的意外退出是安全的,因为语法错误永远不会独立于用户输入而发生.但是,在某些情况下,代码未检测到错误输入的数字会导致使用用户不想使用的数字进一步处理批处理文件.

The batch file using this code is safe against an unwanted exit of batch file processing because of a syntax error never occurs independent on what the user inputs. But a by mistake wrong input number is in some cases not detected by the code resulting in processing the batch file further with a number which the user did not want to use.

这篇关于Windows 批处理文件中的安全数字比较的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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