Reputation: 127
I want to create a script that sets specific values, then writes each value into a new line of a text document. After that it should read the document and set new values to a specified line of the text document, then echo those out.
I have tried different values for "skip=#" which didn't change anything. When I tried to not use the "skip=0" option in the first FOR
and that makes the batch echo out "Value three" for all values. (Quick edit: I've used this website for information on it so far.)
@ECHO OFF
REM Setting values
SET @valueone=Value one
SET @valuetwo=Value two
SET @valuethree=Value three
REM Saving values
IF EXIST "values.txt" DEL "values.txt"
echo %@valueone% >values.txt
echo %@valuetwo% >>values.txt
echo %@valuethree% >>values.txt
REM Reading values again and echoing them at at the same time.
REM This was separated (first reading then echoing) but it didn't change anything.
FOR /F "skip=0 delims=" %%i IN (values.txt) DO SET @valueonefinal=%%i
echo Value number one:
echo %@valueonefinal%
echo.
FOR /F "skip=1 delims=" %%i IN (values.txt) DO SET @valuetwofinal=%%i
echo Value number two:
echo %@valuetwofinal%
echo.
FOR /F "skip=2 delims=" %%i IN (values.txt) DO SET @valuethreefinal=%%i
echo Value number three:
echo %@valuethreefinal%
pause
Expected output in the console:
Value number one:
Value one
Value number two:
Value two
Value number three:
Value three
Actual output:
delims=" was unexpected at this time.
Value number one:
ECHO is off.
Value number two:
Value three
Value number three:
Value three
I'm not that experienced but I suspect that I may be doing the "skip=#" part wrong. Any help with this is greatly apprechiated!
Upvotes: 3
Views: 2304
Reputation: 3264
Your problem is that you are parsing the File from Top to bottom, and skipping the First value, what you don't realize is that FOR will set the value to the LAST item it found. This means that the script as written can only ever return the last item in the values file.
To deal with this you could:
I like to Break the loop.
First let me make you code a little more streamlined so we can re-write it multiple times to show each
This is going to work exactly as your existing code but now we can easily add more values and loop them in a quick go.
@( SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ValuesFile=%~dp0values.txt"
REM Remove Old Values File
DEL /F /Q "!_ValuesFile!" >NUL 2>NUL
REM Saving values
FOR %%A IN (one two three) DO (
ECHO.Value %%A>>"!_ValuesFile!" )
)
CALL :Main
( PAUSE
ENDLOCAL
EXIT /B 0
)
:Main
FOR /L %%L IN (0,1,2) DO (
CALL SET /A "_Value=%%L + 1"
ECHO.&ECHO.------ Iteration: %%L ------&ECHO.Value number !_Value!:
IF %%L EQU 0 ( SET "_ForOptions=tokens=*" ) ELSE (
SET "_ForOptions=Skip=%%L tokens=*" )
CALL :Loop %%L
)
GOTO :EOF
:Loop
FOR /F "%_ForOptions%" %%i IN (' type "%_ValuesFile%"
') DO ( CALL SET "@value%_Value%final=%%i" )
ECHO.!@value%_Value%final!
GOTO :EOF
@( SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ValuesFile=%~dp0values.txt"
REM Remove Old Values File
DEL /F /Q "!_ValuesFile!" >NUL 2>NUL
REM Saving values
FOR %%A IN (one two three) DO (
ECHO.Value %%A>>"!_ValuesFile!" )
)
CALL :Main
( PAUSE
ENDLOCAL
EXIT /B 0
)
:Main
FOR /L %%L IN (0,1,2) DO (
CALL SET /A "_Value=%%L + 1"
ECHO.&ECHO.------ Iteration: %%L ------&ECHO.Value number !_Value!:
IF %%L EQU 0 ( SET "_ForOptions=tokens=*" ) ELSE (
SET "_ForOptions=Skip=%%L tokens=*" )
CALL :Loop %%L
)
ECHO.&ECHO.------ Final Values After %%L Iterations: ------
SET @value
GOTO :EOF
:Loop
FOR /F "Tokens=*" %%A IN ('
CMD /C "FOR /F %_ForOptions% %%i IN (' type "%_ValuesFile%" ') DO @(ECHO.%%i&exit /b)"
') DO @(
SET "@value%_Value%final=%%~A"
)
ECHO.!@value%_Value%final!
GOTO :EOF
Y:\>C:\Admin\S-O_Value-Checker_v2.cmd
------ Iteration: 0 ------
Value number 1:
Value one
------ Iteration: 1 ------
Value number 2:
Value two
------ Iteration: 2 ------
Value number 3:
Value three
------ Final Values After %L Iterations: ------
@value1final=Value one
@value2final=Value two
@value3final=Value three
Press any key to continue . . .
Upvotes: 4
Reputation: 34919
The option skip=0
is not accepted by the for /F
command, the specified number must be in the range from 1 to 231 − 1. To skip no lines just do not provide the skip
option at all.
You seem to try to assign the text of a certain line to a variable (for instance, the third one):
FOR /F "skip=2 delims=" %%i IN (values.txt) DO SET @valuethreefinal=%%i
Well, this actually assigns the content of the last line to the variable, because the set
command in the body of the loop is executed for all but the skipped lines. More precisely said, the for /F
loop iterates over all non-empty lines which do not begin with ;
which is the default character of the eol
option.
To actually assign the third line to the variable you need to change the code:
rem // Ensure that the variable is initially unset somewhere before:
set "@valuethreefinal="
rem // As soon as the variable is set the `if` condition is no longer going to be fulfilled:
for /F "usebackq skip=2 delims=" %%i in ("values.txt") do if not defined @valuethreefinal set "@valuethreefinal=%%i"
This does not necessarily assign the third line to the variable, it actually assigns the text of the first line after the (two) skipped ones that is not empty and does not begin with ;
(remember the eol
character).
The usebackq
option allows to put quotation marks around the file name. This is not necessary in your situation, but it is when a file name contains SPACEs or other special characters.
I used the undocumented quoted set
syntax here because this is safer than the unquoted one, particularly when it comes to special characters and also to avoid unintended trailing white-spaces.
To disable the eol
character you could use the undocumented unquoted option string syntax:
for /F usebackq^ skip^=2^ delims^=^ eol^= %%i in ("values.txt") do if not defined @valuethreefinal set "@valuethreefinal=%%i"
As you can see the SPACEs and =
-signs are escaped by the caret symbol ^
in order to treat the whole option string as a unit.
This still skips over empty lines though. To prevent this take a loop at this thread: preserve empty lines in a text file while using batch for /f.
Since you want to capture more than a single line you could extend the code to the following:
set "@valueonefinal=" & set "@valuethreefinal=" & set "@valuethreefinal="
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
if not defined @valueonefinal (
set "@valueonefinal=%%i"
) else (
if not defined @valuetwofinal (
set "@valuetwofinal=%%i"
) else (
if not defined @valuethreefinal (
set "@valuethreefinal=%%i"
)
)
)
)
This can be compressed to:
set "@valueonefinal=" & set "@valuethreefinal=" & set "@valuethreefinal="
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
if not defined @valueonefinal (
set "@valueonefinal=%%i"
) else if not defined @valuetwofinal (
set "@valuetwofinal=%%i"
) else if not defined @valuethreefinal (
set "@valuethreefinal=%%i"
)
)
A more flexible method is to use pseudo-arrays:
rem // Initialise an index counter:
set /A "INDEX=0"
rem // Assign every line to an element of a pseudo-array:
for /F usebackq^ delims^=^ eol^= %%i in ("values.txt") do (
rem // Increment the index counter:
set /A "INDEX+=1"
rem // Assign the current line to a pseudo-array element:
call set "@valuefinal[%%INDEX%%]=%%i"
)
The (non-empty) lines of the file value.txt
are now assigned to variables called @valuefinal[1]
, @valuefinal[2]
, @valuefinal[3]
, etc. (there is no concept of arrays in batch scripting, the variables are exactly the same as yours, @valueonefinal
, etc., that is why I use the term "pseudo").
The call
command is used here in order to be able to write and read the variable INDEX
within the same block of code; just using set "@valuefinal[%INDEX%]=%%i"
would result in assigning and therefore overwriting the variable @valuefinal[0]
in every loop iteration.
Upvotes: 4