我最近发现了帖子 查找子字符串是否在字符串中(而不是在文件中),其中指出考虑到
@setlocal enableextensions enabledelayedexpansion
@echo off
set str1=%1
if not x%str1:bcd=%==x%str1% echo It contains bcd
endlocal
然后
相等双方之前
x
是确保字符串bcd
正常工作。它还可以防止某些"不当"的起始字符。
但是,我还没有找到任何关于这个x
的实际效果的解释。那么x"%string%"
和"%string%"
有什么区别呢?
这只是一个非常糟糕的编码字符串比较。通过两端的x
,即使 Windows 命令处理器在执行命令IF之前用空字符串解析整个命令行时替换了%str1:bcd=%
或%str1%
,也可以比较这两个字符串。
但是批处理文件的执行仍然被cmd.exe
立即退出,因为在环境变量的值str1
包含空格字符或"&<>|
的情况下存在语法错误。
将参数字符串括在双引号中会导致获得除百分号以外的所有字符,并且启用延迟环境变量扩展时,感叹号也被解释为文字字符,包括解释为参数字符串分隔符的双引号字符串之外的空格。
更好的是:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" goto EndBatch
set "str1=%~1"
if not "%str1:bcd=%" == "%str1%" echo It contains bcd
:EndBatch
endlocal
首先比较批处理文件的第一个参数,不要用空字符串将双引号括起来。因此,如果批处理文件在没有任何参数的情况下启动,或者仅""
作为第一个参数字符串,Windows 命令处理器将执行命令GOTO,从而恢复使用命令SETLOCAL在堆栈上推送的先前环境并退出批处理文件。
否则,实际上使用参数字符串调用批处理文件。此参数字符串分配给环境变量str1
并删除周围的双引号(如果有)。因此,在调用带有参数test
的批处理文件时,test
的值被分配给环境变量str1
,并且在调用它时,"another test"
调用它时,没有双引号的值another test
被分配给str1
。即使在使用错误的编码参数字符串"bcd test
调用批处理文件(缺少第二个"
)时,也只是将bcd test
分配给环境变量str1
。
IF条件将环境变量str1
的值与使用未修改的变量值删除的所有bcd
匹配值进行比较。两个字符串周围的双引号使得即使在包含空格或与号或重定向运算符<>|
上也可以比较两个字符串。命令IF在比较两个字符串时包含双引号。
那么这段代码现在安全吗?
不,如果有人将批处理文件称为无效,test_bcd"
作为缺少第一个双引号的参数字符串,则不会发生这种情况。在这种情况下,cmd.exe
执行的第一个IF命令行是:
if "test_bcd"" == "" goto EndBatch
错误指定参数字符串的尾随"
不会被cmd.exe
删除,并在执行时在此命令行上导致语法错误,因为在命令提示符窗口中运行批处理文件时可以看到,第一行修改为@echo on
。
不使用延迟环境变量扩展的一种解决方案是:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "str1=%~1"
if not defined str1 goto EndBatch
set "str1=%str1:"=%"
if not defined str1 goto EndBatch
if not "%str1:bcd=%" == "%str1%" echo It contains bcd
:EndBatch
endlocal
此代码确保str1
在执行比较字符串的IF命令之前不包含任何双引号。
另一种解决方案是使用延迟环境变量扩展:
@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "str1=%~1"
if not "!str1:bcd=!" == "!str1!" echo It contains bcd
endlocal
上面的代码看起来更好,没有使用延迟的环境变量扩展。但是,如果参数字符串例如"!Hello!"
,则它不会按预期工作,因为在这种情况下,if not
条件也为真,因此输出是消息It contains bcd
,尽管字符串!Hello!
不包含bcd
。
解决方案是:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "str1=%~1"
setlocal EnableDelayedExpansion
if not "!str1:bcd=!" == "!str1!" echo It contains bcd
endlocal
endlocal
将参数字符串分配给环境变量时不会启用延迟扩展str1
这会导致在字符串中获取感叹号"!Hello!"
解释为文字字符。然后启用延迟扩展以与使用延迟环境变量扩展进行字符串比较,从而避免命令行本身在执行IF命令之前被cmd.exe
修改。
要了解使用的命令及其工作原理,请打开命令提示符窗口,在那里执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。
call /?
......解释%~1
,不如这里做的那么好。echo /?
endlocal /?
goto /?
if /?
set /?
setlocal /?
另请参阅:
- Windows 命令解释器 (CMD.EXE) 如何解析脚本?长答案 每个批处理文件编写者都应该从上到下仔细阅读。
- forfiles - FALSE vs. false(不适用于小写?这个答案是关于命令IF的参数处理的。
- 符号等同于 Windows 批处理文件中的 NEQ、LSS、GTR 等......此答案详细解释了命令IF如何完成字符串比较。
- 为什么在命令行上使用"set var = text"后没有带有"echo %var%"的字符串输出?这个答案解释了为什么
set "variable=value"
应该在一般情况下使用,而不是其他变体。 - 单行包含多个命令使用 Windows 批处理文件...解释双引号参数字符串外的
&
和||
如何由cmd.exe
解释。 - Microsoft篇关于使用命令重定向运算符的文章解释了
cmd.exe
如何解释双引号参数字符串之外的<>|&
。
在字符串前面添加x
(或任何其他字母字符)可确保关系语句在语法上有效,即使字符串为空。
假设str1
是一个空字符串。然后替换后的比较%str1:bcd=%==%str1%
退化为==
,这在语法上是无效的。
但是,如果前面有x
,比较就变得x==x
并且可以进行评估。当然,向两个字符串中的每一个添加相同的前缀不会影响它们的(不)相等。