在 Windows Batch 中,为啥用括号括起来的 for 循环不会扩展它被告知要回显的变量?
Posted
技术标签:
【中文标题】在 Windows Batch 中,为啥用括号括起来的 for 循环不会扩展它被告知要回显的变量?【英文标题】:In Windows Batch, why a for loop surrounded by parenthesis won't expand a variable it's told to echo?在 Windows Batch 中,为什么用括号括起来的 for 循环不会扩展它被告知要回显的变量? 【发布时间】:2021-08-22 03:54:11 【问题描述】:在我的项目中,我需要使用findstr
解析命令的多行输出。为此,我按照this answer 创建了一个行数组,并使用for
循环将它们逐个echo
:
(for /L %n in (1 1 %info_count%) do echo %info[%n]%)| findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"
问题是这会导致echo
部分输出%info[1]%
、%info[2]%
等等。
但是,删除第一组括号会导致 for
循环的 do
部分同时解释 echo
和管道部分,这意味着通过一个管道而不是六个 echo
输出,我通过六个不同的管道获得六个(扩展的)echo
输出。
是什么导致了这个问题?
PS:上面的 sn-p 正是我在整个脚本运行后在结果提示中调查的内容。在主项目中,我自然会为我的变量和批处理文件循环内的适当变量使用延迟扩展(即setlocal enabledelayedexpansion
、!var!
和%%a
编辑:鉴于jeb's answer,我添加了更长的sn-p,并且我理解问题在于findstr
是一个外部exe。
for /f %%d in ('^(for /L %%n in ^(1 1 !info_count!^) do echo !info[%%n]!^)^| ^(findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"^)^| find /c /v ""') do (
if [%%d]==[0] (
...
) else (
...
其目的是分析 mkvmerge -i
的输出,这是一个已放入数组(!info1!
、!info2
等)的多行文本,使用 findstr
检测两个所需匹配项之一,然后使用find
输出匹配的数量,这将决定下一行的行为。
我试图通过这一切实现的是避免多次启动 mkvmerge,但我已经有了一个可行的替代方案,它只是再次调用 mkvmerge 而不是 for /l
循环(这意味着双管道正在工作另一种情况)。
This answer,感谢jeb,建议在findstr
周围添加括号可以解决问题。对我来说不是这样。
【问题讨论】:
你必须在由 FOR 控制的变量中使用 double %:FOR /L %%n (....)
否则 %n
会被自己替换,......什么都没有。
@user207421 命令是在命令行执行的,所以符号百分号是正确的
【参考方案1】:
在命令行上测试这个没有用,因为行为与批处理文件不同。
这里的主要问题是百分比扩展。它在解析块时被扩展!
下一个问题是百分比表达式的嵌套不能以这种方式完成。
%info[%n]%
分为%info[%
和n]%
。
在批处理文件中,您将使用延迟扩展来避免所有这些问题。
(for /L %%n in (1 1 !info_count!) do echo !info[%%n]!) | findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"
但它仍然失败,因为您正在将一个延迟扩展的块输送到findstr
。
管道使用默认配置启动两个子 cmd.exe 进程,然后再次禁用延迟扩展。
Why does delayed expansion fail when inside a piped block of code?
您可以将 findstr 移动到块中,然后将 !info[%%n]!
展开,然后将其传输到新的 cmd.exe 实例。
for /L %%n in (1 1 !info_count!) do (
echo !info[%%n]! | findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"
)
这会比较慢,因为它会启动 findstr 和每个循环的管道。
在您提供更多信息后:
包围 findstr
在这里没有帮助,因为那不是你的问题。
如果您直接启动 findstr
或在 cmd.exe 实例中启动并不会改变行为,那么只有在管道 findstr
部分中存在百分比扩展时才需要该技巧。
如果您只想计算匹配项,则可以不使用find
。
set /a match_cnt=0
for /L %%n in (1 1 %info_count%) do (
echo !info[%%n]! | findstr /R /C:"[0-9s]: [0-9]" /C:"bytes" > nul
if !errorlevel! == 0 set /a match_cnt+=1
)
echo matches: !match_cnt!
您还可以修改您的初始代码以使用:
(
for /L %%n in (1 1 %info_count%) do @(
cmd /V:on /C echo !info[%%n]!
)
) | findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"
或者没有cmd /v:on
的(缓慢)调用也一样
call echo %%^^info[%%n]%%
【讨论】:
首先我不认为%info[%n]% is being split, as my testing shows
%n%` 扩展了。其次,将findstr
移动到块中会遇到另一个问题,我将不得不编辑我的问题并使用更大的 sn-p 来说明原因。第三,您提出的调查路线使我相信findstr
是一个外部exe 是问题所在。然而,提供的解决方案,将其括起来,并没有帮助。所有这些都非常有帮助,我明天会继续调查,谢谢!
@FabioFreitas 您可以在之前使用set info[=WRONG split
测试拆分行为以上是关于在 Windows Batch 中,为啥用括号括起来的 for 循环不会扩展它被告知要回显的变量?的主要内容,如果未能解决你的问题,请参考以下文章