While trying to provide a 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"
This does not return a match, hence nothing is echoed and ErrorLevel
becomes set to 1
.
Though when I go through the parsing process step by step, I conclude the opposite, I do expect a match and an ErrorLevel
of 0
, because:
at first, the whole line is parsed and immediate (
%
) expansion takes place, hence%vData%
becomes expanded and%%
become replaced by%
, resulting in the following command line that is going to be executed:echo/...;%main%\Programs\Go\Bin| findstr /I /C:"%main%\\Programs\\Go\\Bin"
each side of the pipe
|
is executed in its own newcmd
instance bycmd /S /D c
, both of which run incmd
context (which affects handling of%
-expansion), resulting in these parts:left side of the pipe:
echo/...;C:\Main\Programs\Go\Bin
right side of the pipe:
findstr /I /C:"C:\Main\\Programs\\Go\\Bin"
(the search string that
findstr
finally uses isC:\Main\Programs\Go\Bin
as the\
is used as escape character even in literal search mode,/C
or/L
)
As you can see the final search string actually occurs within the echoed string, therefore I expect a match, but the command lines does not return one. So what is going on here, what do I miss?
When I clear the variable main
before executing the pipe command line, I get the expected result, which leads me to the conclusion that the variable main
becomes not expanded despite my assumption above (note that in cmd
context, %main%
is kept literally when the variable is empty). Am I right?
It is getting even more confusing: when I put the right side of the pipe in between parentheses, a match is returned, independent on whether or not the variable main
is defined:
echo/%vData%| (findstr /I /C:"%%main%%\\Programs\\Go\\Bin")
Can anyone explain this? Could this be connected to the fact that findstr
is an external command, opposed to echo
? (I mean, break | echo/%vData%
expands the value of main
as expected if defined...)
In a CMD script changes things.
My Quick and Dirty understanding of the Reason:
The batch is behaving as expected, there is no need to double up the percents on
main
even on the other side of a pipe, because there is no expansion happening, it is a normal variable.IE the order of operations you write is correct, but also is happening on each line individually, I think you are just so close to the problem you didn't notice or am I not understanding the issue?
I am not sure if this is clearly stated so I plan to review what I believe is happening step by step:
Add in Main
Now that Main is defined lets Echo vData Again:
So this side was relying on the expansion but the FindStr side isn't because %main% was already defined so it was expanded on the first pass.
When parsed by the CMD interpreter, I believe the sequence will be as follows:
Alternatively, When parsed by the CMD interpreter, I believe the sequence will be as follows:
If your concern is that you were actually storing
%%Main%%
in a variable, say the%_Regex%
Variable:Then generally you'd have to either Wrap the statements on the other side of the Pipe in a Parenthesis or Call findStr in my experience.
IE:
Results:
This is what I woudl normally expect given my experience with CMD as well.
I simplified your example to:
The output is:
This proves that using an exe-file in a pipe results in different behaviour than using an internal batch command.
Only for internal commands a new cmd.exe instance will be created.
That's also the cause why the findstr doesn't expand the percent signs.
This confusing line expands, because the parentheses forces a new cmd.exe instance.
I will modify the explanation at 5.3 Pipes - How does the Windows Command Interpreter (CMD.EXE) parse scripts?