andry81 / contools

A wide range of scripts for Windows interpreter (cmd.exe) and other interpreters such as bash shell (.sh), visual basic (.vbs), jscript (.js), python (.py), perl (.pl) and so on. Plus some set of standalone console utilities and tools aside other utilities and tools from cygwin, msys and mingw. • :page_with_curl: https://github.com/andry81-stats/contools--gh-stats :page_with_curl:
https://sf.net/p/contools
MIT License
11 stars 0 forks source link

Batch FOR-Loops EOL relies on a rare character #3

Open jeb-de opened 6 days ago

jeb-de commented 6 days ago

In many batch files is a for-loop used like this

FOR /F "EOL=<rare-character> tokens=* delims="

It's used to fetch a full line and avoid skipping of lines, when they begin with the EOL character.

But using a rare-character has some drawbacks

All points can be solved by using an empty EOL character. To create such a FOR-loop, quoting must not be used

FOR /F tokens^=*^ delims^=#^ EOL^= %%L in (somefile) do echo ...%%L

Or when a string used, it can be even more simplified by enclosing the string in quotes and remove them later by using the %%~ syntax.

FOR /F "delims=" %%L in (""!string!"") do echo ...%%~L

Btw. "tokens=* delims=" can be simplified to "delims="

andry81 commented 6 days ago

FOR /F "delims=" %%L in (""!string!"") do echo ...%%~L

What the difference with this:

FOR /F "delims=" %%L in ("!string!") do echo ...%%L
jeb-de commented 6 days ago

This sample shows the difference:

@echo off
setlocal EnableDelayedExpansion
SET "string=;Hello"
FOR /F "delims=" %%L in ("!string!") do echo FAIL: %%L
FOR /F "delims=" %%L in (""!string!"") do echo WORK: %%~L

Only the second FOR works, because the string always begins with a quote. Therefore there can never a problem with the EOL character. And the loop even works for empty strings, because they are enclosed in quotes

andry81 commented 6 days ago

FOR /F tokens^=*^ delims^=#^ EOL^= %%L in (somefile) do echo ...%%L

I think you can shorten that to this:

FOR /F "tokens=* delims=#"^ EOL^= %%L in (somefile) do echo ...%%L
jeb-de commented 6 days ago

I think you can shorten that to this: FOR /F "tokens=* delims=#"^ EOL^= %%L in (somefile) do echo ...%%L

Yes, that works too and is even a bit simpler

andry81 commented 6 days ago

Only the second FOR works, because the string always begins with a quote.

Yes, by default the ; is a quote character, so you can redefine it to empty:

FOR /F "delims="^ EOL^= %%L in ("!string!") do echo FAIL: %%L

And the loop even works for empty strings, because they are enclosed in quotes

Interesting, but must be tested on all Windows versions. Not sure it is stable.

jeb-de commented 6 days ago

Interesting, but must be tested on all Windows versions. Not sure it is stable.

I am quite sure that this already worked in XP. This is only another way to solve the EOL problem, for me it looks a bit better than the empty EOL variant and in some situations it is convenient that it can handle empty strings.

andry81 commented 5 days ago

for me it looks a bit better than the empty EOL variant

It has an issue :smile:

@echo off
setlocal
set string=!
setlocal ENABLEDELAYEDEXPANSION
FOR /F "delims=" %%L in (""!string!"") do echo WORK: %%~L?
jeb-de commented 5 days ago

Not really 😄 The exclamation mark only disappears just after the expansion in "WORK: %%~L", because delayed expansion was active. For-Meta-variables always have this problem, because of the order of expansions.

@echo off
setlocal DisableDelayedExpansion
set string=!
setlocal ENABLEDELAYEDEXPANSION
FOR /F "delims=" %%L in (""!string!"") do (
    endlocal
    echo WORK: %%~L?
)
andry81 commented 4 days ago

How about the case with a file read? You still have to use the empty EOL.

andry81 commented 4 days ago

I've tested the ""!string!"" on XP/7/8, x86/x64. Seems works the same way for all 256 characters.