为什么在其搜索字符串 return 中包含变量扩展的 `findstr` 在涉及管道时会出现意外结果?
Why does `findstr` with variable expansion in its search string return unexpected results when involved in a pipe?
同时尝试提供 comprehensive answer to the question Why is FindStr returning not-found, I encountered strange behaviour of code that involves a pipe. This is some code based on the original question (to be executed in a batch-file):
rem // Set variable `vData` to literally contain `...;%main%\Programs\Go\Bin`:
set "vData=...;%%main%%\Programs\Go\Bin"
set "main=C:\Main"
echo/%vData%| findstr /I /C:"%%main%%\Programs\Go\Bin"
这 不 return 匹配,因此没有回显并且 ErrorLevel
设置为 1
。
虽然当我逐步完成解析过程时,我得出了相反的结论,但我确实希望匹配和 0
的 ErrorLevel
,因为:
首先,整行被解析并立即(%
)展开,因此 %vData%
被展开并且 %%
被 [= 取代20=],导致将要执行以下命令行:
echo/...;%main%\Programs\Go\Bin| findstr /I /C:"%main%\Programs\Go\Bin"
管道|
的每一侧都由cmd /S /D c
在自己的新cmd
实例中执行,两者运行都在cmd
上下文(影响 %
-扩展的处理),导致这些部分:
管道左侧:
echo/...;C:\Main\Programs\Go\Bin
管道右侧:
findstr /I /C:"C:\Main\Programs\Go\Bin"
(findstr
最终使用的搜索字符串是 C:\Main\Programs\Go\Bin
因为 \
即使在文字搜索模式下也被用作转义字符,/C
或 /L
)
如您所见,最终搜索字符串实际上出现在回显字符串中,因此我希望匹配,但命令行没有 return。那么这是怎么回事,我错过了什么?
当我在执行管道命令行之前清除变量 main
时,我得到了预期的结果,这使我得出结论,尽管我有上面的假设,但变量 main
没有扩展(请注意,在 cmd
上下文中, %main%
在变量为空时按字面意思保留)。我说得对吗?
它变得更加混乱:当我将管道的右侧放在括号之间时,一个匹配项是 returned,与变量 main
是否定义无关:
echo/%vData%| (findstr /I /C:"%%main%%\Programs\Go\Bin")
谁能解释一下?这是否与 findstr
是一个外部命令而不是 echo
这一事实有关? (我的意思是,break | echo/%vData%
扩展了 main
的值,如果定义...)
我将您的示例简化为:
@echo off
set "main=abc"
break | findstr /c:"111" %%main%%
break | echo findstr /c:"222" %%main%%
输出为:
FINDSTR: %main% kann nicht geöffnet werden.
findstr /c:"222" abc
这证明在管道中使用 exe 文件会导致与使用内部批处理命令不同的行为。
仅为内部命令创建一个新的 cmd.exe 实例。
这也是 findstr 不扩展百分号的原因。
这条令人困惑的行展开了,因为括号强制了一个新的 cmd.exe 实例。
break | (findstr /c:"111" %%main%%)
在 CMD 脚本中更改内容。
我对原因的粗略理解:
批次的行为符合预期,即使在管道的另一侧也不需要将 main
上的百分比加倍,因为没有发生扩展,它是一个正常变量。
IE 你写的操作顺序是正确的,但也在每一行上单独发生,我认为你太接近你没有注意到的问题或者我不理解这个问题?
我不确定是否明确说明了这一点,所以我打算逐步回顾一下我认为正在发生的事情:
set "vData=...;%%main%%\Programs\Go\Bin"
内存中存储的结果:“...;%main%\Programs\Go\Bin
”
ECHO
ing %vData%
产生:“...;%main%\Programs\Go\Bin
”
在主目录中添加
set "main=C:\Main"
内存中存储的结果:“C:\Main
”
ECHO
ing %main%
产生:“C:\Main
”
ECHO
'%%main%%' 产生:“%C:\Main%
”
既然定义了 Main,就可以再次回显 vData:
Result Stored in Memory is still: "...;%main%\Programs\Go\Bin
"
ECHO
ing "%vData%"
Yields: "...;%main%\Programs\Go\Bin"
CALL ECHO
ing "%vData%"
Yields:
"...;C:\Main\Programs\Go\Bin"
所以这边依赖于扩展,但 FindStr 边不是,因为 %main% 已经定义,所以它在第一次通过时被扩展。
当被CMD解释器解析时,我相信顺序如下:
Given your original Find String:
echo/%vData%| findstr /I /C:"%%main%%\Programs\Go\Bin"
变为:
echo/...;%main%\Programs\Go\Bin| findstr /I /C:"%C:\Main%\Programs\Go\Bin"
变为:
echo/...;C:\Main\Programs\Go\Bin | (findstr /I /C:"%C:\Main%\Programs\Go\Bin")
或者,当被CMD解释器解析时,我相信顺序如下:
Given the modified Regex String:
echo/%vData%| findstr /I /C:"%main%\Programs\Go\Bin"
Becomes:
echo/...;%main%\Programs\Go\Bin| findstr /I /C:"C:\main\Programs\Go\Bin"
Becomes:
echo/...;C:\Main\Programs\Go\Bin | (findstr /I /C:"C:\Main\Programs\Go\Bin")
如果您担心的是您实际上将 %%Main%%
存储在变量中,请说 %_Regex%
变量:
然后根据我的经验,通常您必须将管道另一侧的语句用括号括起来或调用 findStr。
IE:
@(
SETLOCAL
ECHO OFF
)
rem // Set variable `vData` to literally contain `...;%main%\Programs\Go\Bin`:
set "vData=...;%%main%%\Programs\Go\Bin"
rem // Set variable `_Regex` to literally contain `%main%\Programs\Go\Bin`:
SET "_Regex=%%main%%\Programs\Go\Bin"
rem // Set variable `_Regex` to literally contain `C:\Main`:
set "main=C:\Main"
ECHO(&ECHO(Variable Contents After Main Set:&ECHO(
echo(Normal: vData = "%vData%"
echo(Normal: _Regex = "%_Regex%"
echo(Normal: main = "%main%"
CALL echo(CALLed: vData = "%vData%"
CALL echo(CALLed: _Regex = "%_Regex%"
ECHO(&ECHO(Testing the Results of The FindString Methods:
ECHO(==============================================&ECHO(
ECHO( Original ^%%^%%main^%%^%% Method:
echo/%vData%| findstr /I /C:"%%main%%\Programs\Go\Bin"
ECHO(==============================================&ECHO(
ECHO( Using the ^%%_Regex^%% Stored Variable:
echo/%vData%| findstr /I /C:"%_Regex%"
ECHO(==============================================&ECHO(
ECHO( Using just ^%%main^%%:
echo/%vData%| findstr /I /C:"%main%\Programs\Go\Bin"
ECHO(==============================================&ECHO(
ECHO( Using the CALL ^%%_Regex^%% Stored Variable:
echo/%vData%| CALL findstr /I /C:"%_Regex%"
ECHO(==============================================&ECHO(
ECHO( Using the (^%%_Regex^%%) Stored Variable:
echo/%vData%| ( findstr /I /C:"%_Regex%" )
ECHO(==============================================&ECHO(
结果:
C:\Admin>C:\Admin\TestFindStr.cmd
Variable Contents After Main Set:
Normal: vData = "...;%main%\Programs\Go\Bin"
Normal: _Regex = "%main%\Programs\Go\Bin"
Normal: main = "C:\Main"
CALLed: vData = "...;C:\Main\Programs\Go\Bin"
CALLed: _Regex = "C:\Main\Programs\Go\Bin"
Testing the Results of The FindString Methods:
==============================================
Original %%main%% Method:
==============================================
Using the %_Regex% Stored Variable:
==============================================
Using just %main%:
...;C:\Main\Programs\Go\Bin
==============================================
Using the CALL %_Regex% Stored Variable:
...;C:\Main\Programs\Go\Bin
==============================================
Using the (%_Regex%) Stored Variable:
...;C:\Main\Programs\Go\Bin
==============================================
鉴于我对 CMD 的经验,这也是我通常期望的。
同时尝试提供 comprehensive answer to the question Why is FindStr returning not-found, I encountered strange behaviour of code that involves a pipe. This is some code based on the original question (to be executed in a batch-file):
rem // Set variable `vData` to literally contain `...;%main%\Programs\Go\Bin`:
set "vData=...;%%main%%\Programs\Go\Bin"
set "main=C:\Main"
echo/%vData%| findstr /I /C:"%%main%%\Programs\Go\Bin"
这 不 return 匹配,因此没有回显并且 ErrorLevel
设置为 1
。
虽然当我逐步完成解析过程时,我得出了相反的结论,但我确实希望匹配和 0
的 ErrorLevel
,因为:
首先,整行被解析并立即(
%
)展开,因此%vData%
被展开并且%%
被 [= 取代20=],导致将要执行以下命令行:echo/...;%main%\Programs\Go\Bin| findstr /I /C:"%main%\Programs\Go\Bin"
管道
|
的每一侧都由cmd /S /D c
在自己的新cmd
实例中执行,两者运行都在cmd
上下文(影响%
-扩展的处理),导致这些部分:管道左侧:
echo/...;C:\Main\Programs\Go\Bin
管道右侧:
findstr /I /C:"C:\Main\Programs\Go\Bin"
(
findstr
最终使用的搜索字符串是C:\Main\Programs\Go\Bin
因为\
即使在文字搜索模式下也被用作转义字符,/C
或/L
)
如您所见,最终搜索字符串实际上出现在回显字符串中,因此我希望匹配,但命令行没有 return。那么这是怎么回事,我错过了什么?
当我在执行管道命令行之前清除变量 main
时,我得到了预期的结果,这使我得出结论,尽管我有上面的假设,但变量 main
没有扩展(请注意,在 cmd
上下文中, %main%
在变量为空时按字面意思保留)。我说得对吗?
它变得更加混乱:当我将管道的右侧放在括号之间时,一个匹配项是 returned,与变量 main
是否定义无关:
echo/%vData%| (findstr /I /C:"%%main%%\Programs\Go\Bin")
谁能解释一下?这是否与 findstr
是一个外部命令而不是 echo
这一事实有关? (我的意思是,break | echo/%vData%
扩展了 main
的值,如果定义...)
我将您的示例简化为:
@echo off
set "main=abc"
break | findstr /c:"111" %%main%%
break | echo findstr /c:"222" %%main%%
输出为:
FINDSTR: %main% kann nicht geöffnet werden.
findstr /c:"222" abc
这证明在管道中使用 exe 文件会导致与使用内部批处理命令不同的行为。
仅为内部命令创建一个新的 cmd.exe 实例。
这也是 findstr 不扩展百分号的原因。
这条令人困惑的行展开了,因为括号强制了一个新的 cmd.exe 实例。
break | (findstr /c:"111" %%main%%)
在 CMD 脚本中更改内容。
我对原因的粗略理解:
批次的行为符合预期,即使在管道的另一侧也不需要将 main
上的百分比加倍,因为没有发生扩展,它是一个正常变量。
IE 你写的操作顺序是正确的,但也在每一行上单独发生,我认为你太接近你没有注意到的问题或者我不理解这个问题?
我不确定是否明确说明了这一点,所以我打算逐步回顾一下我认为正在发生的事情:
set "vData=...;%%main%%\Programs\Go\Bin"
内存中存储的结果:“
...;%main%\Programs\Go\Bin
”
ECHO
ing%vData%
产生:“...;%main%\Programs\Go\Bin
”
在主目录中添加
set "main=C:\Main"
内存中存储的结果:“
C:\Main
”
ECHO
ing%main%
产生:“C:\Main
”
ECHO
'%%main%%' 产生:“%C:\Main%
”
既然定义了 Main,就可以再次回显 vData:
Result Stored in Memory is still: "
...;%main%\Programs\Go\Bin
"
ECHO
ing"%vData%"
Yields:"...;%main%\Programs\Go\Bin"
CALL ECHO
ing"%vData%"
Yields:"...;C:\Main\Programs\Go\Bin"
所以这边依赖于扩展,但 FindStr 边不是,因为 %main% 已经定义,所以它在第一次通过时被扩展。
当被CMD解释器解析时,我相信顺序如下:
Given your original Find String:
echo/%vData%| findstr /I /C:"%%main%%\Programs\Go\Bin"
变为:
echo/...;%main%\Programs\Go\Bin| findstr /I /C:"%C:\Main%\Programs\Go\Bin"
变为:
echo/...;C:\Main\Programs\Go\Bin | (findstr /I /C:"%C:\Main%\Programs\Go\Bin")
或者,当被CMD解释器解析时,我相信顺序如下:
Given the modified Regex String:
echo/%vData%| findstr /I /C:"%main%\Programs\Go\Bin"
Becomes:
echo/...;%main%\Programs\Go\Bin| findstr /I /C:"C:\main\Programs\Go\Bin"
Becomes:
echo/...;C:\Main\Programs\Go\Bin | (findstr /I /C:"C:\Main\Programs\Go\Bin")
如果您担心的是您实际上将 %%Main%%
存储在变量中,请说 %_Regex%
变量:
然后根据我的经验,通常您必须将管道另一侧的语句用括号括起来或调用 findStr。
IE:
@(
SETLOCAL
ECHO OFF
)
rem // Set variable `vData` to literally contain `...;%main%\Programs\Go\Bin`:
set "vData=...;%%main%%\Programs\Go\Bin"
rem // Set variable `_Regex` to literally contain `%main%\Programs\Go\Bin`:
SET "_Regex=%%main%%\Programs\Go\Bin"
rem // Set variable `_Regex` to literally contain `C:\Main`:
set "main=C:\Main"
ECHO(&ECHO(Variable Contents After Main Set:&ECHO(
echo(Normal: vData = "%vData%"
echo(Normal: _Regex = "%_Regex%"
echo(Normal: main = "%main%"
CALL echo(CALLed: vData = "%vData%"
CALL echo(CALLed: _Regex = "%_Regex%"
ECHO(&ECHO(Testing the Results of The FindString Methods:
ECHO(==============================================&ECHO(
ECHO( Original ^%%^%%main^%%^%% Method:
echo/%vData%| findstr /I /C:"%%main%%\Programs\Go\Bin"
ECHO(==============================================&ECHO(
ECHO( Using the ^%%_Regex^%% Stored Variable:
echo/%vData%| findstr /I /C:"%_Regex%"
ECHO(==============================================&ECHO(
ECHO( Using just ^%%main^%%:
echo/%vData%| findstr /I /C:"%main%\Programs\Go\Bin"
ECHO(==============================================&ECHO(
ECHO( Using the CALL ^%%_Regex^%% Stored Variable:
echo/%vData%| CALL findstr /I /C:"%_Regex%"
ECHO(==============================================&ECHO(
ECHO( Using the (^%%_Regex^%%) Stored Variable:
echo/%vData%| ( findstr /I /C:"%_Regex%" )
ECHO(==============================================&ECHO(
结果:
C:\Admin>C:\Admin\TestFindStr.cmd
Variable Contents After Main Set:
Normal: vData = "...;%main%\Programs\Go\Bin"
Normal: _Regex = "%main%\Programs\Go\Bin"
Normal: main = "C:\Main"
CALLed: vData = "...;C:\Main\Programs\Go\Bin"
CALLed: _Regex = "C:\Main\Programs\Go\Bin"
Testing the Results of The FindString Methods:
==============================================
Original %%main%% Method:
==============================================
Using the %_Regex% Stored Variable:
==============================================
Using just %main%:
...;C:\Main\Programs\Go\Bin
==============================================
Using the CALL %_Regex% Stored Variable:
...;C:\Main\Programs\Go\Bin
==============================================
Using the (%_Regex%) Stored Variable:
...;C:\Main\Programs\Go\Bin
==============================================
鉴于我对 CMD 的经验,这也是我通常期望的。