在试图修复嵌套延迟扩展问题时,在函数的else情况中添加for循环导致的无法解释的行为



前言

我是批处理的新手,已经做了大约两周了。当我向函数添加一个特定的for循环时,它没有遵循我认为的正常程序流规则。当删除for循环后,函数会遵循我期望的程序流,但输出仍然不是我想要的。根本问题是延迟展开变量中的延迟展开变量。

我已经检查了多次括号,在底部附近的完整代码中找不到任何不平衡的地方。在程序流方面,这毫无意义,添加for循环会影响到for循环之前的语句。我唯一能想到的是,发生了一些奇怪的错误,代码根本没有执行完整的循环,并在循环后立即到达我的暂停语句。

我实际上想做什么

我正试图用来分隔一个单行字符串;在字符串中使用未知/可变数量的标记和其他可能的分隔符(如逗号)来分隔LATER。这不能用批处理预编码的for循环来完成,for/F只能同时执行行和/或设置标记数,而常规的for循环则用分号和逗号分隔。我想先用分号分隔,然后用逗号分隔。为了做到这一点,我正试图用标签制作自己的简单拆分循环。我遇到了一个涉及变量扩展延迟的问题。此处示例:

不是完整的代码,而是延迟扩展问题的代码

setlocal EnableDelayedExpansion
set "TestArgument=%~1"
set /a "CurrentIndex=0"
set /a "DelimIndex=0"
set /a "LoopCount=0"
:ForEach
IF "!TestArgument:~%CurrentIndex%,1!"=="" (goto :ForEachEnd & echo End of list)
echo testing semicolon
IF NOT "!TestArgument:~%CurrentIndex%,1!"==";" (SET /a CurrentIndex=%CurrentIndex%+1 & goto :ForEach & echo found non ;) ELSE (
set /a "LengthToSearch=%CurrentIndex%-%DelimIndex%"
SET "currentArg=!TestArgument:~%DelimIndex%,!LengthToSearch!!

我想从功能的这个特定部分得到什么

示例输入:

set "ICMPv4RuleTest="Enable Echo Ping Request ICMPv4;Protocol,ICMPv4;Enabled,Yes;LocalIP,Any;RemoteIP,Any;Direction,In;Action,Allow""

所需输出的示例:For循环(上面的标签),其中每个迭代使用一个由分隔的字符串;因此第一次迭代将取CCD_ 1值。以上的全部要点是以一种方式获取输入和子字符串,以便每次迭代都由分号分隔

上面的代码,特别是最后一行导致了问题,因为(我认为)程序将其读取为延迟扩展!TestArgument:~%DelimIndex%,!(这会出错)!!(这算不上什么)只留下LengthToSearch作为原始字符串。我想要的是在子字符串操作之前展开LengthToSearch值,然后展开/run/dodo子字符串操作的展开,并将其设置为CurrentArg(由;分隔的单行字符串)。我不能将%符号与LengthtoSearch一起使用,因为它似乎与子字符串操作在同一范围内。但是我可以将%符号和DelimIndex一起使用(因为这超出了范围?我不知道)。

我尝试过的

我尝试了子字符串行(最后一行)的变体,包括:

  • SET "currentArg=!TestArgument:~%DelimIndex%,%LengthToSearch%!不打印任何内容
  • SET "currentArg=!TestArgument:~%DelimIndex%,!!LengthToSearch!!!打印31(DelimIndex的字面参数)
  • SET "currentArg=%TestArgument:~%DelimIndex%,!!LengthToSearch!!%不打印任何内容
  • SET "currentArg=%TestArgument:~%DelimIndex%,!LengthToSearch!%不打印任何内容

我试过其他一些,但肯定不对。我认为应该是

  • SET "currentArg=!TestArgument:~%DelimIndex%,!LengthToSearch!!

方式,但这只是前面提到的问题。我在这里做了一些研究,发现其他人在字符串搜索和替换方面有类似于我的问题,他们使用FOR循环将延迟扩展值插入到延迟扩展子字符串操作中,作为FOR循环参数。我想我可以使用同样的方法,所以我尝试了这个(对最后一行进行了重大更改):

setlocal EnableDelayedExpansion
set "TestArgument=%~1"
set /a "CurrentIndex=0"
set /a "DelimIndex=0"
set /a "LoopCount=0"
:ForEach
IF "!TestArgument:~%CurrentIndex%,1!"=="" (goto :ForEachEnd & echo End of list)
echo testing semicolon
IF NOT "!TestArgument:~%CurrentIndex%,1!"==";" (SET /a CurrentIndex=%CurrentIndex%+1 & goto :ForEach & echo found non ;) ELSE (
set /a "LengthToSearch=%CurrentIndex%-%DelimIndex%"
FOR /F "tokens=*" %%A IN ("!LengthToSearch!") DO (SET "currentArg=!TestArgument:~%DelimIndex%,%%A!)

就在那时,事情变得非常奇怪。由于某些原因,forloop的添加导致整个ELSE情况无法执行。当我运行代码时,

IF NOT "!TestArgument:~%CurrentIndex%,1!"==";"

只"运行"一次(它并没有真正运行它应该运行的所有内容),然后循环就真的中断了。我的意思并不是说它会进入循环的末尾——它只是完全忽略了函数的其余部分。我已经在完整的代码中检查了多次括号这里:

完整的功能代码

:FirewallRuleTestFunc
echo got to function
setlocal EnableDelayedExpansion
set "TestArgument=%~1"
ECHO PARAM WAS "%~1"
echo TestArgument was "%TestArgument%"
set /a "CurrentIndex=0"
set /a "DelimIndex=0"
set /a "LoopCount=0"
echo trying foreach
:ForEach
echo got to foreach
IF "!TestArgument:~%CurrentIndex%,1!"=="" (goto :ForEachEnd & echo End of list)
echo testing semicolon
IF NOT "!TestArgument:~%CurrentIndex%,1!"==";" (SET /a CurrentIndex=%CurrentIndex%+1 & goto :ForEach & echo found non ;) ELSE (
echo WHY IS THIS NOT BEING REACHED.
echo CurrentIndex is %CurrentIndex%
echo startposition was first: %DelimIndex%
set /a "LengthToSearch=%CurrentIndex%-%DelimIndex%"
echo length to search was: !LengthToSearch!
echo length to search was: %LengthToSearch%
echo startposition was: %DelimIndex%
FOR /F "tokens=*" %%A IN ("!LengthToSearch!") DO (SET "currentArg=!TestArgument:~%DelimIndex%,%%A!)
echo loop of ; Arg was: !currentArg!
set /a DelimIndex=!CurrentIndex!  
echo Delim index was: %DelimIndex%
IF !LoopCount! NEQ 0 (
echo afterfirstloop
FOR /F "tokens=1,2 delims=," %%A IN ('netsh advfirewall firewall show rule name^="!RuleName!" ^| FIND "%%A"') DO (
echo loop of , A was '%%A'
SET "FoundArgument=%%B"
SET "FirewallRuleExists=%%A"
IF "!FirewallRuleExists!"=="" (echo TEST FAILED firewall rule with name "!RuleName!" NOT FOUND)
IF "!FoundArgument!"=="Yes" (echo Test Passed) ELSE (echo Test Failed was "!FoundArgument!")
)
) ELSE (SET "RuleName=!currentArg!" & set /a "LoopCount=%LoopCount%+1" & echo firstloop done)
echo got past if else
)
:ForEachEnd
echo end of THING
endlocal
echo SOMETHING
goto:EOF

无论是第二次中频的ELSE病例(IF NOT "!TestArgument:~%CurrentIndex%,1!"==";")中的回声,还是除了第二次IF之前的回声之外的任何其他回声,都没有真正发生过。这对我来说毫无意义,我无法理解为什么当添加for循环时它会忽略程序流,而当注释掉for循环时程序流会起作用。我所想做的就是有一个for循环,每个我可以指定的分隔符循环一次。我唯一的猜测是发生了一些错误,导致它直接进入EOF。在我的函数被调用后,会有一个暂停,因为我的程序的其余部分应该是无关的。我目前正致力于这一部分。该功能的输入如下所示:

函数调用示例:

set "ICMPv4RuleTest="Enable Echo Ping Request ICMPv4;Protocol,ICMPv4;Enabled,Yes;LocalIP,Any;RemoteIP,Any;Direction,In;Action,Allow""
call :FirewallRuleTestFunc %ICMPv4RuleTest%

我非常沮丧,我只需要一些帮助。我会继续尝试各种各样的东西,但这是我需要完成的程序的最后一部分,我只想获得稳定、有效和高效的东西,而不必像以前那样,使用行列和幻数来通过令牌检查防火墙设置。(我正在测试一个脚本来配置一些设置,以便可以建立公司网络)。

如果您想以分号拆分字符串,可以使用以下简单方法:

@echo off
setlocal
set "ICMPv4RuleTest=Enable Echo Ping RequestICMPv4;Protocol,ICMPv4;Enabled,Yes;LocalIP,Any;RemoteIP,Any;Direction,In;Action,Allow"
echo Original string: "%ICMPv4RuleTest%"
echo/
echo Split string at semicolons:
for %%a in ("%ICMPv4RuleTest:;=" "%") do echo %%a

输出:

Original string: "Enable Echo Ping Request ICMPv4;Protocol,ICMPv4;Enabled,Yes;Lo
calIP,Any;RemoteIP,Any;Direction,In;Action,Allow"
Split string at semicolons:
"Enable Echo Ping Request ICMPv4"
"Protocol,ICMPv4"
"Enabled,Yes"
"LocalIP,Any"
"RemoteIP,Any"
"Direction,In"
"Action,Allow"

我看到你喜欢做多项测试和实验,所以我把这个简单方法的解释留给你;)

如果你想知道如何将一个变量的展开组合起来,并将其用于另一变量的进一步展开,那么我建议你阅读这个答案。

出了什么问题

好吧,我发现了问题。我的FOR循环语句中有一个拼写错误。线路:

FOR /F "tokens=*" %%A IN ("!LengthToSearch!") DO (SET "currentArg=!TestArgument:~%DelimIndex%,%%A!)

在我的文件中,导致问题的SET调用中有不平衡的引号(")。因此,这导致了程序崩溃,没有完成其余的功能,给我带来了很大的困惑。我通过尝试这个找到了它

FOR /F "tokens=*" %%A IN ("!LengthToSearch!") DO (echo %%A)

程序流程如预期般正常工作。因此,我重新调查了for循环,发现了缺失的引号。

嵌套延迟表达式变量呢

所建议的for循环方法(在某个地方,无法通过谷歌进行再融资)确实解决了嵌套延迟扩展问题。我不知道你能把变量嵌套到多深。但不管怎样,做:

FOR /F "tokens=*" %%A IN ("!LengthToSearch!") DO (SET "currentArg=!TestArgument:~%DelimIndex%,%%A!)

而不是:

SET "currentArg=!TestArgument:~%DelimIndex%,!LengthToSearch!!

实际工作如预期!它会在两个索引值都正确插入的情况下拆分字符串。我不记得在哪里提到过它,但我记得解释是这样的:FOR循环扩展发生在延迟变量扩展之前,但发生在%符号的初始扩展之后。因此,我可以将for循环参数放入延迟扩展操作中,它就会起作用。我不知道为什么它会起作用,但这就是我对它的理解。如果我能找到更多关于这个问题发生的原因和我刚刚如何解决它的信息,我将编辑这个答案。

郑重声明,我讨厌这种无声的错误。我真的希望有更好的错误处理,因为批脚本功能强大,但不必要地困难,因为几乎没有好的错误检查。如果记事本++在变量中显示不平衡的引号就好了。但对于未来的参考,至少这解决了尝试使用嵌套延迟扩展变量的任何问题。解决方案是简单地使用for循环变量作为中介。

如果有人找到了一个很好的参考来解释延迟变量扩展行为和循环行为,我很乐意进行必要的编辑,这样人们就不会再遇到这个问题了

相关内容

最新更新