0

前言

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

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

我实际上正在尝试做的事情

我正在尝试用 ; 分隔单行字符串 在我想稍后分隔的字符串中使用未知/可变数量的标记和其他可能的分隔符(如逗号)。这不能通过批处理预先制作的 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 循环(上面的标签),其中每次迭代都采用一个由 ; 分隔的字符串。所以第一次迭代会Enable Echo Ping Request ICMPv4取值。上面的重点是获取输入并以某种方式对其进行子串化,以便每次迭代都由分号分隔

上面的代码,特别是最后一行导致了问题,因为(我认为)程序将其读取为延迟扩展!TestArgument:~%DelimIndex%,!(这会出错)然后!(这没什么)只留下 LengthToSearch 作为原始字符串。我想要的是在子字符串操作之前扩展 LengthToSearch 值,然后扩展/运行/执行子字符串操作的扩展并将其设置为 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

并且第二个 IF ( ) 的 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%

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

4

2 回答 2

1

什么问题

好吧,我发现了问题。我的 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 循环方法(在某处,无法通过 google 重新找到)确实解决了嵌套延迟扩展问题。我不知道你可以嵌套多深的变量。但无论如何,做:

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

代替:

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

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

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

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

于 2018-10-26T14:44:37.977 回答
1

如果你想在分号处分割一个字符串,你可以使用这个简单的方法:

@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"

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

如果您想知道如何组合一个变量的扩展以在另一个变量的进一步扩展中使用它,那么我建议您阅读这个答案

于 2018-10-27T13:47:40.747 回答