简体   繁体   中英

In Windows Batch, why a for loop surrounded by parenthesis won't expand a variable it's told to echo?

In my project, I need to parse a multiline output from a command with findstr . To do so, I followed this answer to create an array of lines and echo them one by one using a for loop:

(for /L %n in (1 1 %info_count%) do echo %info[%n]%)| findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"

The problem is this leads to the echo part outputting %info[1]% , %info[2]% and so on.

Removing the first set of parenthesis, however, leads to the do part of the for loop interpreting both the echo and the piped part, which means that instead of six echo outputs through one pipe, I get six (expanded) echo outputs through six different pipes.

What is causing this issue?

PS: the snippet above is just what I used to investigate in the resulting prompt after my whole script has run. In the main project, naturally I'm using delayed expansion for my variables and proper variables inside loops for batch files (ie setlocal enabledelayedexpansion , !var! and %%a


Edit: in light of jeb's answer , I'm adding a longer snippet, as well as my understanding that the problem lies in findstr being an external 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 (
        ...

It's aim is to analyze the output of mkvmerge -i , a multiline text that has been put in an array ( !info1! , !info2 etc), use findstr to detect one of two desired matches and then use find to output the number of matches, which will dictate the behavior in the next lines.

What I'm trying to achieve with all of this is to avoid launching mkvmerge multiple times, but I already had a working alternative that simply called mkvmerge again instead of the for /l loop (which means the double piping was working in another scenario).

This answer , which I found thanks to jeb , suggests adding the parentheses around findstr would solve the problem. That was not the case for me.

Testing this on the command line isn't useful, because the behavior differs from batch files.

The main problem here is the percent expansion. It's expanded while a block is parsed!

The next problem is that nesting of percent expressions can't be done this way. %info[%n]% is splitted into %info[% and n]% .

In a batch file you would use delayed expansion to avoid all this problems.

(for /L %%n in (1 1 !info_count!) do echo !info[%%n]!) | findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"

But it still fails, because you are piping a block with delayed expansion to findstr . Piping starts two child cmd.exe processes with the default configuration and then the delayed expansion is disabled again. Why does delayed expansion fail when inside a piped block of code?

You could move the findstr into the block, then the !info[%%n]! will be expanded before it's transferred to the new cmd.exe instance.

for /L %%n in (1 1 !info_count!) do (
  echo !info[%%n]! | findstr /R /C:"[0-9s]: [0-9]" /C:"bytes"
)

This will be slower, because it starts findstr and the pipe for each loop.

After your additional informations:

Bracketing findstr doesn't help here, because that's not your problem.
If you start findstr directly or within a cmd.exe instance doesn't change the behavior, that trick is only necessary when there are percent expansions in the piped findstr part.

If you only want to count the matches, that can be done without 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!

You could also modify your initial code to work with:

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

Or the same without the (slow) invocation of cmd /v:on

    call echo %%^^info[%%n]%%

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM