当涉及管道时,为什么在搜索字符串中具有可变扩展名的`findstr`返回意外结果?

asc*_*pfl 5 windows cmd batch-file findstr io-redirection

在试图为为什么FindStr返回未找到问题提供全面的答案时,我遇到了涉及管道的代码的奇怪行为。这是一些基于原始问题的代码(将在执行):

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"
Run Code Online (Sandbox Code Playgroud)

这并没有返回匹配,因此没有什么呼应和ErrorLevel被置1 1

尽管当我逐步进行解析过程时,我得出相反的结论,但是我确实期望有一个匹配项和一个匹配ErrorLevel0,因为:

  1. 首先,将对整个行进行解析,然后进行即时(%)扩展,因此%vData%将其扩展并%%替换为%,从而将执行以下命令行:

    echo/...;%main%\Programs\Go\Bin| findstr /I /C:"%main%\\Programs\\Go\\Bin"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 管道的每一端|都在自己的新cmd实例中执行cmd /S /D c,这两个实例均在cmd上下文中运行(这会影响%-expansion的处理),从而产生以下部分:

如您所见,最终的搜索字符串实际上发生在回显的字符串内,因此我希望有一个匹配项,但是命令行不会返回一个。那么这里发生了什么,我想念什么?


当我main在执行管道命令行之前清除变量时,得到了预期的结果,这使我得出结论,main尽管我做了上面的假设,但变量并未扩展(请注意,在cmd上下文中,%main%当变量为空时将保留该变量)。我对吗?


它变得更加令人困惑:当我将管道的右侧放在括号之间时,无论是否main定义了变量,都会返回一个匹配项:

echo/%vData%| (findstr /I /C:"%%main%%\\Programs\\Go\\Bin")
Run Code Online (Sandbox Code Playgroud)

谁能解释一下?可以将其与findstr外部命令相对echo吗?(我的意思是,如果定义,break | echo/%vData%则按main预期扩展值...)

jeb*_*jeb 4

我将你的例子简化为:

\n\n
@echo off\n\nset "main=abc"\nbreak | findstr /c:"111" %%main%%\nbreak | echo findstr /c:"222" %%main%%\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出是:

\n\n
\n

FINDSTR: %main% kann nicht ge\xc3\xb6ffnet werden。
\n findstr /c:"222" abc

\n
\n\n

这证明在管道中使用 exe 文件会导致与使用内部批处理命令不同的行为。
\n仅对于内部命令,才会创建新的 cmd.exe 实例。
\n这也是 findstr 不扩展百分号的原因。

\n\n

这条令人困惑的行会扩展,因为括号会强制创建一个新的 cmd.exe 实例。

\n\n
break | (findstr /c:"111" %%main%%)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我将修改5.3 管道 - Windows 命令解释器 (CMD.EXE) 如何解析脚本?\n

\n

  • @aschipfl 感谢这个好发现,我很惊讶之前没有人绊倒过这个 (2认同)
  • 是的,这是一个非常有趣的发现!我已经更新了 cmd.exe 解析规则。有趣的是,以下内容只需要一次转义:`echo "&" | 查找str ^&`。但括号形式需要双重转义:`echo "&" | (查找str ^^^&)`。更新后的规则一切都有意义。 (2认同)
  • 此行为仅适用于管道。我确认 `for /f .... in ('someCommand') do ...` 总是调用 cmd.exe 来执行 someCommand,即使 someCommand 是外部命令。管道设计的一个很好的结果 - 当通过“cmd /c”显式执行命令时,没有第二个隐式“cmd /c”。所以像“break |”之类的东西 cmd /v:on /c echo !cmdcmdline!` 非常高效。 (2认同)