var inside for /f loop’s in (…) clause

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Sponge Belly
Posts: 235
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

var inside for /f loop’s in (…) clause

#1 Post by Sponge Belly » 02 Jul 2013 07:10

Hello All! :-)

The little snippet below outputs the number and contents of any lines found in a user-specified file that don’t contain a Line Feed. And then it does it again.

First using a bare findstr, and then using findstr wrapped inside a for /f loop’s in (…) clause.

Code: Select all

@echo off & setlocal enableextensions
set file="%~1"
(set lf=^

)
set nl=^^^%lf%%lf%^%lf%%lf%

echo(bare findstr
findstr /nv %nl% %file%

echo(%nl%%nl%findstr wrapped inside for /f loop's in (...^) clause
for /f "delims=" %%f in ('findstr /nv %%nl%% %file%') do echo(%%f

endlocal & exit /b 0


The bare findstr only requires %nl%, but inside the loop’s in (…) clause it is necessary to write %%nl%%. On the other hand, %file% works just fine in both cases.

What’s going on here? Can someone please explain? :?:

- SB

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: var inside for /f loop’s in (…) clause

#2 Post by dbenham » 02 Jul 2013 08:02

The CMD parser strips newline characters from input unless they are escaped. That is why you need to use the escaped %NL% variable instead of the plain %LF%. Note that delayed expansion occurs after the newline stripping, which is why !LF! normally works (although not in this case :twisted: )

The command inside the FOR IN() clause is executed in a new CMD thread, so it undergoes another round of parsing.

With only one set of percents, the variable is expanded in the parent batch process and the child thread strips the newline.

With two sets of percents, the variable expansion is delayed until within the child thread, so everything works as desired.

Delayed expansion will not work in this case because the expansion still occurs within the parent batch process.


Dave Benham

Sponge Belly
Posts: 235
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

Re: var inside for /f loop’s in (…) clause

#3 Post by Sponge Belly » 02 Jul 2013 13:13

Thanks Dave!

The scary thing is that I actually understood your explanation. :shock: Another little piece of the puzzle has fallen into place. ;-)

Anyways, for anyone still reading this topic, to make the LF variable work inside the for /f loop’s in (…) clause using delayed expansion, you would have to do the following:

Code: Select all

for /f "delims=" %%f in ('
cmd /v:on /c findstr /nv ^^!lf^^! %file%
') do echo(%%f


It’s all thrashed out in this DT topic from December, 2012. Mind-bending stuff. You may need to lie down after reading, but worth it! :-)

- SB

jeb
Expert
Posts: 1062
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: var inside for /f loop’s in (…) clause

#4 Post by jeb » 04 Jul 2013 01:20

Hi Dave,

dbenham wrote:The command inside the FOR IN() clause is executed in a new CMD thread, so it undergoes another round of parsing.

Correct :)

dbenham wrote:With only one set of percents, the variable is expanded in the parent batch process and the child thread strips the newline.

Not quite correct :twisted:
The child thread doesn't drop the linefeed, the parser stops parsing at the linefeed, all text behind is dropped.
And as it's a pure linefeed, it can't be escaped in any way, but the line can be accessed via the cmdcmdline variable.

Code: Select all

@echo off
set LF=^


set NL=^^^%LF%%LF%^%LF%%LF%
FOR /F "delims=" %%a in ('show.bat %NL% END') DO echo ...%%a


SHOW.bat

Code: Select all

@echo off
setlocal enableDelayedExpansion
(
  echo # "%0"
  echo # cmdcmdline='!cmdcmdline!'
) > CON


I use the redirection to CON here, as it would be hard to transfer a text with a linefeed to the parent process.

Post Reply