for /F not working as expected, not giving every element - for-loop

I have a simple .bat with content:
#echo off
set "var=VAR1?VAR2?VAR3"
echo.%var%
echo.
for /F "delims=?" %%H in ('echo.%var%') do echo.%%~H
I would expect the following output:
VAR1?VAR2?VAR3
VAR1
VAR2
VAR3
Instead I get:
VAR1?VAR2?VAR3
VAR1
^ notice a trailing empty new line
Tried using different delimiter, usebackq, giving eol character, running through another for loop with different parameters, many other things; different variable name, different filename, setlocal w/o and w/ delayed expansion; no success.
If I include the tokens=1,2 parameter, it prints VAR1 into %%H and VAR2 into %%I as it should, but I need it to be "dynamic", give every element my %var% has.
EDIT: What is the most confusing to me is that if instead of writing my elements into a variable separated by a ?, I write it to a file, each element in its own line, then read the file line-by-line with "for /F "delims=*" %%H in ('type "VAR.txt"') do..."
it works perfeclty. Is it because the "delims=*", or is there a higher power messing with me? I don't see any difference from the FOR's perspective. If, by default, it should only give me the first token, why does it give every token from the file? Isn't token as new line the same as token as any other character?

You can also use this very simple trick that converts tokens into lines:
#echo off
setlocal EnableDelayedExpansion
set "var=VAR1?VAR2?VAR3"
echo %var%
echo/
for %%n in (^"^
%Do not remove this line%
^") do for /F "delims=" %%H in ("!var:?=%%~n!") do echo/%%~H
In this way, each token in the string: VAR1?VAR2?VAR3 is converted into separate lines like these ones:
VAR1
VAR2
VAR3
This is done replacing each ? character in the string by a NewLine (ASCII 10) character.
After that, you can process each line in any way you wish...

Each line read by a for/f is split into "tokens", substrings separated by any of the delimiters.
By default, tokens=1, so just the first token on the line is assigned to the metavariable %%H.
You could list each required token in a tokens= clause.
To assign each to %%H in turn, try
for %%H in (%var:?= %) do echo %%H
or if your elements contain spaces,
for %%H in ("%var:?=" "%") do echo.%%~H
The 'extra' newline is a red herring. You are misreading the result.

Here's a possible alternative methodology, which should be okay as long as each question mark delimited token does not contain a doublequote or exclamation mark character:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Dehine a variable named var.
Set "var=VAR 1?VAR,2?VAR|3?VAR;4?VAR<5?VAR>6?VAR&7?VAR=8"
Rem view the content of the variable named var.
Set /P "=%var%" 0<NUL & Echo(
Rem Display an optional empty line
Echo(
Rem Undefine and variables beginning with the string var[.
For /F "Delims==" %%G In ('"(Set var[) 2>NUL"') Do Set "%%G="
Rem define a increment counter
Set "i=1"
Rem Enable delayed variable expansion
SetLocal EnableDelayedExpansion
Rem Define variables named var[increment] splitting var at each question mark.
Set "var[%i%]=%var:?=" & Set /A i +=1 & Set "var[!i!]=%"
Rem Disable delayed variable expansion
Rem and display the individual incremented variable values.
For /F "Tokens=1-2,* Delims=[]=" %%G In ('"(Set var[) 2>NUL"') Do (EndLocal
Set /P "=%%I" 0<NUL & Echo()
Rem Display an optional empty line
Echo(
Pause
Please be aware that if there are more than nine tokens, the display order will not currently match the order in the initial variable. adjustments would have to be made in order to do that.
Expected output:
VAR 1?VAR,2?VAR|3?VAR;4?VAR<5?VAR>6?VAR&7?VAR=8
VAR 1
VAR,2
VAR|3
VAR;4
VAR<5
VAR>6
VAR&7
VAR=8
Press any key to continue . . .

Here's another possibility:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Rem Define a variable named var.
Set "var=VAR 1?VAR,2?VAR;3?VAR=4?VAR!5?VAR&6?VAR)7?VAR^8?VAR#9"
Rem Display the content of the variable.
Set /P "=%var%" 0<NUL & Echo(
Rem Display an optional empty line.
Echo(
Rem Display the individual tokens.
For %%G In ("%var:?=","%") Do Set /P "=%%~G" 0<NUL & Echo(
Rem Display an optional empty line.
Echo(
Pause
This allows for an exclamation mark to exist in your file path tokens. It also has the additional benefit of being able to display the tokens in the same order, regardless of the number of them.
I should add that if you expect there to be percent characters in your tokens, you will have to perform an additional step, (due to those having a special meaning in cmd and batch files).
Expected output:
VAR 1?VAR,2?VAR;3?VAR=4?VAR!5?VAR&6?VAR)7?VAR^8?VAR#9
VAR 1
VAR,2
VAR;3
VAR=4
VAR!5
VAR&6
VAR)7
VAR^8
VAR#9
Press any key to continue . . .

Related

How to process pathnames with ! within an for /F loop?

In a complex batch file I want to read in files with paths, among other things, to read them into a variable one after the other separated by spaces.
This works with the following code so far quite well - but only if the path does not contain an exclamation mark.
Even using the setlocal command (enabledelayedexpansion / disabledelayedexpansion) I did not succeed in processing exclamation marks.
Does anyone here have a clever idea to the problem?
The following example batch creates a text file in the current directory and then reads it in a for /F loop.
At the end all three paths from the text file should be in the variable %Output%. But with the exclamation mark.
#echo off
setlocal enabledelayedexpansion
echo This is an example^^! > "textfile.txt"
echo This is a second example^^! >> "textfile.txt"
echo And this line have an ^^! exclamation mark in the middle >> "textfile.txt"
for /F "usebackq tokens=* delims=" %%a in (textfile.txt) do (
set "Record=%%a"
set "Output=!Output!!Record! - "
)
)
echo %Output%
echo !Output!
endlocal
The Output is like this:
This is an example - This is a second example - And this line have an exclamation mark in the middle
But should be like this:
This is an example! - This is a second example! - And this line have an ! exclamation mark in the middle
It is advisable not using delayed variable expansion on processing files and directories, lines in a text file, strings not defined by the batch file itself, or output captured from the execution of a program or a command line. If it is for some reasons necessary to make use of delayed variable expansion inside a FOR loop, there should be first assigned the file/directory name, the line, or the string to process to an environment variable while delayed expansion is disabled and then enable delayed expansion temporary inside the FOR loop.
Here is a batch file demo which can be simply run from within a command prompt window or by double clicking on the batch file. It creates several files for demonstration in the directory for temporary files, but deletes them all before exiting.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo This is an example!> "%TEMP%\TextFile.tmp"
echo This is a second example!>> "%TEMP%\TextFile.tmp"
echo And this line has an exclamation mark ! in the middle.>> "%TEMP%\TextFile.tmp"
set "Output="
(for /F usebackq^ delims^=^ eol^= %%I in ("%TEMP%\TextFile.tmp") do set "Line=%%I" & call :ConcatenateLines) & goto ContinueDemo
:ConcatenateLines
set "Output=%Output% - %Line%" & goto :EOF
:ContinueDemo
cls
echo/
echo All lines concatenated are:
echo/
echo %Output:~3%
set "Output="
del "%TEMP%\TextFile.tmp"
echo File with name ".Linux hidden file!">"%TEMP%\.Linux hidden file!"
echo File with name "A simple test!">"%TEMP%\A simple test!"
echo File with name " 100%% Development & 'Test' (!).tmp">"%TEMP%\ 100%% Development & 'Test(!)'.tmp"
echo/
echo Files with ! are:
echo/
for /F "eol=| tokens=* delims=" %%I in ('dir "%TEMP%\*!*" /A-D /B /ON 2^>nul') do (
set "NameFile=%%I"
set "FileName=%%~nI"
set "FileExtension=%%~xI"
set "FullName=%TEMP%\%%I"
setlocal EnableDelayedExpansion
if defined FileName (
if defined FileExtension (
echo File with ext. !FileExtension:~1!: !NameFile!
) else (
echo Extensionless file: !NameFile!
)
) else echo Extensionless file: !NameFile!
del "!FullName!"
endlocal
)
endlocal
echo/
#setlocal EnableExtensions EnableDelayedExpansion & for /F "tokens=1,2" %%G in ("!CMDCMDLINE!") do #endlocal & if /I "%%~nG" == "cmd" if /I "%%~H" == "/c" set /P "=Press any key to exit the demo . . . "<nul & pause >nul
The output of this batch file is:
All lines concatenated are:
This is an example! - This is a second example! - And this line has an exclamation mark ! in the middle.
Files with ! are:
File with ext. tmp: 100% Development & 'Test(!)'.tmp
Extensionless file: .Linux hidden file!
Extensionless file: A simple test!
The text file example with concatenating lines makes use of a subroutine called from within the FOR loop processing the lines in the text file. The syntax used here is for maximum performance by getting the subroutine as near as possible to the FOR command line. That is important if the FOR loop has to process hundreds or even thousands of items.
The example processing file names enables and disables delayed expansion inside the FOR loop after having assigned all parts of the currently processed file to environment variables. It could be useful to reduce the list of environment variables before processing thousands of files for a better performance on using this method.
Another method is shown in Magoo´s answer using the command CALL to get a command line with referenced environment variables (re)defined inside the loop parsed a second time. I used that method also in the past quite often, but don't that anymore as it is not fail-safe and not efficient. call set results in searching by cmd.exe in current directory and next in all directories of environment variable PATH for a file with name set and a file extension of environment variable PATHEXT. So it results in lots of file system accesses in the background on each iteration of the FOR loop and if there is by chance a file set.exe, set.bat, set.cmd, etc. found by cmd.exe somewhere, the batch file does not work anymore as expected because of running the executable or calling the batch file instead of the (re)definition of the environment variable.
The following answers written by me could be also helpful:
How to read and print contents of text file line by line?
It explains in full details how to process all lines of a text file.
How to pass environment variables as parameters by reference to another batch file?
It explains in full details what the commands SETLOCAL and ENDLOCAL do.
How to pass a command that may contain special characters (such as % or !) inside a variable to a for /f loop?
This is an example of a batch file designed to process video files with any valid file name on any Windows computer very efficient, safe and secure with full explanation.
Well, the main trick is to enable delayed expansion only when it is actually needed and to disable it otherwise. Since you are accumulating multiple strings in a single variable inside of a loop, it becomes a bit more difficult, because you should have delayed expansion disabled during expansion of for meta-variables (like %%a), but enabled when joining the string, leading to setlocal and endlocal statements inside of the loop. The major purpose of these commands is environment localisation, hence any variable changes become lost past endlocal, so a method of tansfering the value beyond endlocal is required, which is incorporated in the following code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* At this point delayed expansion is disabled, so there is no need to
rem escape exclamation marks; moreover a redirected block prevents
rem superfluous file close/reopen operations, and there is no more
rem trailing space written to the file (as in your original approach): */
> "textfile.txt" (
echo This is an example!
echo This is a second example!
echo And this line have an ! exclamation mark in the middle
)
rem // Let us initialise the output variable:
set "Output= - "
rem // Using `usebackq` only makes sense when you want to quote a file path:
for /F "usebackq tokens=* delims=" %%a in ("textfile.txt") do (
rem // Remember that delayed expansion is still disabled at this point:
set "Record=%%a"
rem // For concatenation we need delayed expansion to be enabled:
setlocal EnableDelayedExpansion
set "Output=!Output!!Record! - "
rem /* We need to terminate the environment localisation of `setlocal`
rem inside of the loop, but we would lose any changes in `Output`;
rem therefore let us (mis-)use `for /F`, which is iterated once: */
for /F "delims=" %%b in ("!Output!") do endlocal & set "Output=%%b"
rem /* An often used method to transfer a variable beyond `endlocal` is
rem the line `endlocal & set "Output=%Output%`, but this only works
rem outside of a parenthesised block because of percent expansion. */
)
rem /* Echo out text with delayed expansion enabled is the only safe way;
rem surrounding separators ` - ` are going to be removed; since `Output`
rem was initialised with something non-empty, we do not even need to skip
rem sub-string expansion for the problematic case of an empty string: */
setlocal EnableDelayedExpansion
echo(!Output:~3,-3!
endlocal
endlocal
exit /B
Pew. I finally got it to work.
It works via a workaround using a second text file.
Not pretty, not performant, but it works and is sufficient for my purposes.
#Magoo, thanks for your post.
This is my solution:
#echo off
setlocal enableextensions enabledelayedexpansion
echo This is an example^^!> "textfile.txt"
echo This is a second example^^!>> "textfile.txt"
echo And this line have an ^^! exclamation mark in the middle>> "textfile.txt"
echo.
echo Content of the textfile:
type "textfile.txt"
set output=
del "textfile2.txt" 1> nul 2>&1
setlocal disabledelayedexpansion
for /f "usebackq tokens=* delims=" %%a IN ("textfile.txt") do (
rem Write each line without a newline character into a new text file
echo|set /p "dummy=%%a, ">>"textfile2.txt"
)
endlocal
rem Loading the content of the new text file into the variable
set /p output=<"textfile2.txt"
del "textfile2.txt" 1> nul 2>&1
echo.
echo --------------------------------------------
echo Content of the variable:
set out
endlocal
The output looks like this:
Content of the textfile:
This is an example!
This is a second example!
And this line have an ! exclamation mark in the middle
--------------------------------------------
Content of the variable:
output=This is an example!, This is a second example!, And this line have an ! exclamation mark in the middle,
It's delayedexpansion mode that appears to raise this problem.
#ECHO OFF
setlocal enabledelayedexpansion
echo This is an example^^^! > "textfile.txt"
echo This is a second example^^^! >> "textfile.txt"
echo And this line have an ^^^! exclamation mark in the middle >> "textfile.txt"
TYPE "textfile.txt"
SETLOCAL disabledelayedexpansion
for /F "usebackq tokens=* delims=" %%a in (textfile.txt) do (
set "Record=%%a"
CALL set "Output2=%%Output2%%%%record%% - "
CALL set "Output=%%Output%%%%a - "
SET out
)
)
endlocal&SET "output=%output%"
echo %Output%
echo !Output!
SET out
I've no doubt that with delayedexpansion off, there would be the same problem with %. Just special characters, I suppose.
Note that with endlocal&SET "output=%output%", the set is executed in delayedexpansion mode.

Batch - How to pass a multiline string value between variables

The problem is echo !out! show all lines but echo %data% which assigned from !out! only show first line
#echo off
call :f
echo %data%
:f
setlocal EnableDelayedExpansion
set out=
set NL=^
for /f "delims=" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo !out!
set data=!out!
exit /b 0
What is the correct way to pass the value entirely to other variable which i need to use freely after endlocal?
Thank you #aschipfl's for your answer that clarify that it's impossible to do it without end up in for-in loop again. Actually all that I want is to simplify my code without have to write for-in loop many times by turn it into a subroutine. I've solved it by passing the subroutine to the subroutine instead.
As derived from this thread, you cannot echo a multi-line string using immediate (%-)expansion, because everything after the first line-break is ignored.
To make your script working you need to correct two issues:
before the line endlocal & set data=%out% you must replace every new-line in variable out by an escaped new-line, that is the sequence ^ plus new-line plus new-line, which is exactly the same that you are using for defining the variable NL;
echo %data% truncates the displayed string at the first occurrence of a new-line in the value of variable data, so you need to use set data to show the actual content of the variable (or more precisely said, of all variables whose names begin with data);
Both of these items are commented (rem) in the following code:
#echo off
setlocal EnableDelayedExpansion
set out=
set NL=^
for /F "delims= eol=|" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo * Original variable content:
set out
rem // Replace every new-line by an escaped new-line:
set out=!out:^%NL%%NL%=^^^%NL%%NL%^%NL%%NL%!
echo * Modified variable content:
set out
endlocal & set data=%out%
rem // Do not use `echo` to show true content of variable:
echo * Returned variable content:
set data
echo * Mistaken variable content:
echo %data%
exit /B 0
Although the variable value is now correctly passed over the endlocal barrier, this approach is not exactly brilliant, because it does not allow you to use variable %data% (again because everything after the first line-break is ignored as initially mentioned), unless you have got delayed expansion enabled in the hosting cmd instance, which would permit to use !data!.
Another remaining problem is that special characters in the multi-line string (like ^, &, (, ) and ", <, >, |) may cause syntax errors or other unexpected issues. However, this can be avoided by using a for meta-variable rather than a normal environment variable for passing the variable value beyond the endlocal barrier, because the former are expanded after special character recognition, in contrast to the latter, which are expanded before:
#echo off
setlocal EnableDelayedExpansion
set out=
set NL=^
for /F "delims=" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo # Original variable content:
set out
rem /* Use a `for` meta-variable rather than a normal environment variable to
rem pass the variable value beyond the `endlocal` barrier;
rem a standard `for` loop can be used here, because there are not going to be
rem wildcards `?` and `*` in the variable value since they have already been
rem resolved by `dir`; `for /F` cannot be used here due to the new-lines: */
for %%j in ("!out!") do endlocal & set "data=%%~j"
rem // Do not use `echo` to show true content of variable:
echo # Returned variable content:
set data
echo # Mistaken variable content:
echo %data%
exit /B 0
The problem not being able to use variable %data% remains though.
To be able to use variable %data% with immediate expansion you could however simply store escaped new-lines rather than literal ones in the variable, because upon expansion you will have the intended literal new-line:
#echo off
setlocal EnableDelayedExpansion
set out=
set NL=^
for /F "delims=" %%i in ('dir /b') do (
if defined out set "out=!out!!NL!"
set "out=!out!%%i"
)
echo # Original variable content:
set out
rem // Replace every new-line by an escaped new-line:
set out=!out:^%NL%%NL%=^^^%NL%%NL%^%NL%%NL%!
echo # Modified variable content:
set out
rem /* Use a `for` meta-variable rather than a normal environment variable to
rem pass the variable value beyond the `endlocal` barrier;
rem a standard `for` loop can be used here, because there are not going to be
rem wildcards `?` and `*` in the variable value since they have already been
rem resolved by `dir`; `for /F` cannot be used here due to the new-lines: */
for %%j in ("!out!") do endlocal & set "data=%%~j"
rem // Do not use `echo` to show true content of variable:
echo # Actual variable content:
set data
echo # Parsed variable content:
echo %data%
exit /B 0
But regard that this is only going to work when %data% does not appear within quoted ("") strings.

Tab Delimiter not working Batch Command

I have a batch file which reads and set values from a text file.
But the text file contains tabs for different variables
Batch file command:
for /f "tokens=* delims=<TAB>" %%x in (input.txt) do set %%x
Text File(input.txt):
a=one b=two c=three d=four
But the variables are not being set properly.
Two points here:
The default delims= value include the space and Tab as delimiters, so you don't have to include a delims= option, unless you want to ignore the spaces as delimiters!
Your tokens=* option define one token letter in your for command (%%x in this case) that contain all tokens in the line. If you want to get four tokens you need to specify tokens=1-4, start the tokens-letter with another one, and process each token accordingly:
.
for /f "tokens=1-4" %%a in (input.txt) do (
set "%%a" & set "%%b" & set "%%c" & set "%%d"
)
this works even, if your textfile has several lines (with different / unknown number of tokens):
#echo off
setlocal enabledelayedexpansion
for /f "tokens=*" %%i in (input.txt) do (
set line=%%i
for %%x in ("!line: =","!") do set %%x
)
the trick is: by enclosing a string in quotes, delimiter chars are not treated as delimiters any more. So adding a quote at the beginning and end of the string AND replacing each TAB with "quote comma quote" changes the string to:
"a=one","b=two","c=three","d=four"
which can happily be processed with for %%x ...

loop through file saving to variable

I now have the following bat file working (which allows one to add text to the end of each line of a file) -- please see also:
bat file: Use of if, for and a variable all together
#echo off
setLocal EnableDelayedExpansion
IF EXIST "%FileToModify1%" (
for /f "tokens=* delims= " %%a in (%FileToModify1%) do (
echo %%a Note: certain conditions apply >> "%SaveFile1%"
)
)
However, I would like to save each line to a variable (including the new line symbol(s)) and then echo the variable to a file at the end. Since there are several lines in the file it is really inefficient to save to a file with each line.
I tried googling this, but the answers do not fit my situation...
essentially I need the syntax for concatenating and saving to a variable (cumulatively like "+=" in C#), and also using the new lines...
Actually you do not need to put everything into a variable, you just need to place the redirection at another position.
Try this:
#echo off
setlocal EnableDelayedExpansion
if exist "%FileToModify1%" (
for /F "usebackq delims=" %%a in ("%FileToModify1%") do (
echo %%a Note: certain conditions apply
)
) > "%SaveFile1%"
endlocal
Note that empty lines in the original file are ignored by for /F, so they are not transferred to the new file. Also lines starting with ; are ignored by for /F (unless you change the eol option -- see for /?).
I modified the for /F options:
no delims are allowed, so the each line is output as is (with "tokens=* delims= ", leading spaces are removed from each line if present);
usebackq allows to surround the file specification in "" which is helpful if it contains spaces;
Appendix A
If you still want to store the file content into a variable, you can do this:
#echo off
setlocal EnableDelayedExpansion
rem the two empty lines after the following command are mandatory:
set LF=^
if exist "%FileToModify1%" (
set "FileContent="
for /F "usebackq delims=" %%a in ("%FileToModify1%") do (
set "FileContent=!FileContent!%%a Note: certain conditions apply!LF!"
)
(echo !FileContent!) > "%SaveFile1%"
)
endlocal
The file content is stored in variable FileContent, including the appendix Note: certain conditions apply. LF holds the new-line symbol.
Note:
The length of a variable is very limited (as far as I know, 8191 bytes since Windows XP and 2047 bytes earlier)!
[References:
Store file output into variable (last code fragment);
Explain how dos-batch newline variable hack works]
Appendix B
Alternatively, you could store the file content in a array, like this:
#echo off
setlocal EnableDelayedExpansion
if exist "%FileToModify1%" (
set /A cnt=0
for /F "usebackq delims=" %%a in ("%FileToModify1%") do (
set /A cnt+=1
set "Line[!cnt!]=%%a Note: certain conditions apply"
)
(for /L %%i in (1,1,!cnt!) do (
echo !Line[%%i]!
)) > "%SaveFile1%"
)
endlocal
Each line of the file is stored in an array Line[1], Line[2], Line[3], etc., including the appendix Note: certain conditions apply. cnt contains the total number of lines, which is the array size.
Note:
Actually this is not a true array data type as such does not exist in batch, it is a collection of scalar variables with an array-style naming (Line[1], Line[2],...); therefore one might call it pseudo-array.
[References:
Store file output into variable (first code fragment);
How to create an array from txt file within a batch file?]
you can write the output file in one shot:
(
for /l %%i in (0,1,10) do (
echo line %%i
)
)>outfile.txt
(much quicker than appending each line separately)

Batch: Search for string to skip lines above and write results to new file

I've successfully written a script which takes a string to search for in a specific file, and then outputs the line where it first occurs, and then I take that value into a for-loop and skips parsing that number of lines and write its contents to a new file. However I do not get blank lines which I find quite problematic to solve.
The string I'm searching for is "/]", caches the line number where it occurs, then accumulates it into a variable with comma-seperation. I then take that variable into a for-loop again and retrieve the first occurring value as my final "skip this number of lines"-variable, which I then use at the bottom for-loop which reads that file again and writes its values to a new files and skips that number of lines at the beginning of the file.
So heres the part of the script that does what I'm describing above:
setlocal enabledelayedexpansion
setlocal enableextensions
set live_svn_access_file=c:\csvn\data\conf\svn_access_file
set of=c:\test.txt
for /f "Tokens=1 Delims=:" %%i in ('findstr /n /c:"/]" %live_svn_access_file%') do (
rem cache value in a variable
set line=%%i
rem accumulate data to one variable
if defined line set skip=!skip!, !line!
)
rem strip first two characters in variable ", "
set skip=!skip:~2!
rem strip everything except first value in array
for /f "Tokens=1 Delims=," %%i in ('echo !skip!') do (
rem store value in a variable
set skip=%%i
)
rem calculate lines - 1 (arithmetic)
set /a skip=!skip!-1
set skip=!skip!
if not defined skip set error=Could not automatically find which parts to skip from live svn_access_file && goto error-handler
for /f "Tokens=* Delims= Skip=%skip%" %%i in (%live_svn_access_file%) do (
rem cache value in a variable
set read-input=%%i
rem write and append content of variable to output-file
echo !read-input! >> %of%
)
I've rewritten the script to match the working one, this is the changed script:
setlocal enabledelayedexpansion
setlocal enableextensions
set live_svn_access_file=c:\csvn\data\conf\svn_access_file
set of=c:\test.txt
for /f "Tokens=1 Delims=:" %%i in ('findstr /n /r /c:"\[..*\]" %live_svn_access_file%') do (
rem cache value in a variable
set line=%%i
rem accumulate data to one variable
if defined line set skip=!skip!, !line!
)
rem take the 2nd sections linenumber into a variable, skipping the first [*foo*]
for /f "Tokens=2 Delims=," %%i in ('"echo !skip!"') do (
rem store value in a variable
set skip=%%i
)
rem add 1 line to skip from retrieved value
set /a skip=!skip!-1
rem verify that number of lines to skip has been successfully retrieved, if not go to error-handler
if not defined skip set error=Could not automatically find which parts to skip from live svn_access_file && goto error-handler
if ["%skip%"] LSS ["1"] set error=Number of lines to skip was less than 1, this will most likely fail && goto error-handler
rem read live svn_access_file, but skip X-lines and write to output-file
setlocal DisableDelayedExpansion
for /f "usebackq Delims= Skip=%skip%" %%i in (`"findstr /n ^^ %live_svn_access_file%"`) do (
rem cache value in a variable
set read-input=%%i
rem write and append content of variable to output-file
setlocal EnableDelayedExpansion
rem strip line number prefix
set read-input=!read-input:*:=!
rem write read lines to output-file
echo(!read-input!>>%of%
endlocal
)
You got two main problems, a FOR /F can't read empty lines (as you discover) and if you use delayed expansion you got trouble with exclamation marks and carets in the line set read-input=%%i.
You can solve both, the empty lines with prefixing each line with a line number by using findstr.
The second with the delayed toggling technic.
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ t.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!" This removes the prefix
echo(!var!
ENDLOCAL
)

Resources