Reputation: 152
Sometimes I think my relationship with the Windows command line is weighted in favour of Stockholms syndrome over anthing else. I'm hoping someone else in this world of punishing stuff has an answer for me!
Here's the background and the problem statement: I have a quite large script that does stuff with a for loop that steps through a file searching for a record number, and changing the record while writing the records around in a nice sequential batch way, ie records are written up to the target record; a changed record is written; the remaining records are written. I needed to add a new action verb to the existing set, which requires an additional IF
clause. It suddenly stopped writing out remaining records after the found record had been changed. Couldn't find the answer.
So I started stripping away code until I reached this residual script. The IF
clause in the FOR/ in / DO
loop ... Leave it in, the script stalls after the written record as per the first sample below; leave it out, the FOR
loop happily does it's thing, as per the second sample output. It gets stranger. Adding in script gives error messages, sometimes running through to completion, sometimes not ~ even a simple echo will give a parsing error. Placement in the script also seems to matter, which made debugging an absolute nightmare.
Clearly there is something that is triggering the command processor to quit the loop. SO I would pose the following two questions to the community:
What is causing the FOR /in /DO
structure to stop processing records
What gives with the echo statements giving parsing errors? (echoing a variable name with a script line number, or just the number, for instance, is very low impact. )
Thanks. Code follows, any text file can be used as the second parameter, the first is a line number to action.
echo off
SETLOCAL enabledelayedexpansion
SET _filein=%~2
SET _line=%1
set _cnt=0
:: _filein = any file, _line = the line to be inspected, _cnt = loop counter
:: in the loop: _record = line value, _msg= display message if line is found
FOR /f "tokens=1 delims=" %%i IN ('type %_filein%') DO (
SET _record=%%i
SET /a _cnt+=1
IF [!_cnt!]==[%_line%] (
echo the line %_line% is !_record!
set _msg=the line is !_record! at position !_cnt!
goto sayrecord
)
echo not the line:
:sayrecord
echo !_cnt!- !_record!
)
:Xit
echo:
IF [%_msg%]==[] (
echo: left out
) ELSE (
echo: I'm in to see %_msg%
)
ENDLOCAL
exit /b
1. With the IF statement: (note parsing error statement)
H>_a 3 _files.txt
H>echo off
not the line:
1- notes.bat
not the line:
2- notesqa.bat
the line 3 is bulkfiledelete.bat
3- bulkfiledelete.bat
line was unexpected at this time.
H>
Without the IF:
H>_a 3 _files.txt
H>echo off
not the line:
1- notes.bat
not the line:
2- notesqa.bat
not the line:
3- bulkfiledelete.bat
not the line:
4- CheckAdminRights.bat
not the line:
: : :
not the line:
47- Reset connection 3 - renew.lnk
not the line:
48- ============
left out
H>
Upvotes: 3
Views: 288
Reputation: 38623
Given that your stated intent was to replace the content of %2
on line %1
, you don't need delayed expansion or to set
all of those variables:
@For /F "Tokens=1*Delims=]" %%I In ('Type "%~2"^|"%__AppDir__%find.exe" /V /N ""')Do @If "%%I"=="[%~1%" (Echo Inserted line content)Else Echo=%%J
@Pause
You would modify the content after Echo
and before )
to the replacement line content, and the last line is added just to ensure that you can read the output.
@For /F "Tokens=1*Delims=]" %%I In ('Type "%~2"^|"%__AppDir__%find.exe" /V /N ""')Do @If "%%I"=="[%~1" ("%__AppDir__%choice.exe" /M "Replace %%J with new content"&If ErrorLevel 2 (Echo=%%J)Else Echo Inserted line content)Else Echo=%%J
@Pause
Upvotes: 1
Reputation: 152
OK, so I have a code block that works thanks to @jwdonaahue for the initial clue; and @SomethingDark for the problem statement.
The :label
is the problem here, although it works in many other production scripts it clearly is problematic. Instead of a script that drops to the bottom of the loop for processing, putting in an IF
clause for each use case is what is needed.
@echo off
SETLOCAL enabledelayedexpansion
SET _filein=%~2
SET _line=%1
set _cnt=0
:: _filein = any file, _line = the line to be inspected, _cnt = loop counter
:: in the loop: _record = line value, _msg= display message if line is found
FOR /f "tokens=1 delims=" %%i IN ('type %_filein%') DO (
SET _record=%%i
SET /a _cnt+=1
IF [!_cnt!]==[%_line%] (
echo the line %_line% is !_record!
set "_msg=the line is !_record! at position !_cnt!"
echo not the line:
CALL :sayrecord
)
IF [!_cnt!] neq [%_line%] CALL :sayrecord
)
:Xit
echo:
IF [%_msg%]==[] (
echo: left out
) ELSE (
echo: I'm in to see %_msg%
)
ENDLOCAL
exit /b
:sayrecord
echo !_cnt!- !_record!
exit /b
i.e. to solve my problem,
IF
clause for each action verb;I'll also go through all the scripts and do a maint on them!
Upvotes: 1