skip=!count! is not working in windows(7) batch file - batch-file

In for loop i have checked condition, if condition is true i have set the count value and skipped some lines in file,
#echo off
SetLocal EnableDelayedExpansion
set /a count=0
set for_parameters="skip=!count! delims="
for /f %for_parameters% %%a in ('list.txt') do (
echo %%a
if %%a==Exception: (
set /a count+=2
)
)
Endlocal
Its shows delims=" was unexpected at this time. Error
Can anyone help me to fix this problem .....

What jeb said about delayed expansion and FOR options is true - you can't use it - but that has nothing to do with the problem in your code.
You are using delayed expansion before you use the expression in your FOR statement, so there is no problem there.
Your problem is that you are attempting to set SKIP=0. The SKIP value must be >0 to be valid. The fix is simple: don't include the SKIP option if you don't want to skip any lines :-)
#echo off
SetLocal EnableDelayedExpansion
set /a count=0
set "skip="
if !count! gtr 0 set "skip=skip=!count!"
set for_parameters="!skip! delims="
for /f %for_parameters% %%a in ('list.txt') do (
echo %%a
if %%a==Exception: (
set /a count+=2
)
)
Endlocal
Expanding on jeb's point: you cannot do the following
for /f !for_parameters! %%a in ...
because FOR has special parsing rules. Most commands parse their options after delayed expansion. But FOR and IF parse their options before FOR variable expansion and delayed expansion take place. Neither FOR variables nor delayed expansion can be used to specify FOR or IF options.

In the for-options you can only use percent expansion, but not delayed expansion.
If your skip value itself is calculated in a block, then you need to extract the for loop into a function

You can use the More command to skip lines.
#echo off
:: By Elektro H#cker
SetLocal EnableDelayedExpansion
set /a count=2
for /F %%a in ('Type "list.txt" ^| MORE +!COUNT!') do (echo %%a)
Pause&exit

Related

Semicolons at beginning of the line, delims and FOR cycle

I cannot figure out how to correctly identify an empty/undefined variable having two semicolon one after one other or the line that starts with it.
This is the cycle:
for /F "delims=; tokens=1-7" %%m IN (testlist.txt) DO echo FUNCGROUP=%%r a=%%m b=%%n c=%%o d=%%p e=%%q f=%%s
I also tried adding "eol=;" and "......eol=" without success.
This is content of the first line of the file testlist.txt:
;xxxxxx;Active;;FALSE;con ter - dong;HWID000001;Item;sites/coll-
The result I need is, for the first cycle:
a=
b=xxxxxx
c=Active
d=
e=FALSE
f=con ter - dong
g=HWID000001
Thanks for any help.
To achieve that, you need to put a "NULL value" for empty fields in the lines of the file.
As there is no direct string substitution utilities with batch, you have to make substitutions beforehand to create "empty" fields. I suggest you to use a "NULL" character for empty fields like unbreakable space (Alt+0160).
In your case, this gives :
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "eol= tokens=*" %%l in (file.txt) DO (
SET "LINE=%%l"
SET "LINE=###!LINE:;;=; ;!###"
SET "LINE=!LINE:###;= ;!"
SET "LINE=!LINE:;###=; !"
SET "LINE=!LINE:###=!"
for /F "delims=; tokens=1-7" %%m in ("!LINE!") DO (
SET "RES=FUNCGROUP=%%r a=%%m b=%%n c=%%o d=%%p e=%%q f=%%s"
echo !RES: =!
)
)
Note that the SET "LINE=###!LINE:;;=; ;!###", SET "LINE=!LINE:###;= ;!" and the SET "LINE=!LINE:;###=; !" sections use the unbreakable space (Alt+0160) and replace beginning ";" with "Alt+0160;", the ending ";" with ";Alt+0160" and any following ";;" with ";Alt+0160;". The for loop parses then correctly the line, and next you just have to remove the unbreakable space to get "empty" variables.
EDIT: As brightly suggested by #jeb in the comments, you can also use quotes to handle empty fields. Each for loop variables can be then directly and simply unquoted.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "eol= tokens=*" %%l in (file.txt) DO (
SET "LINE=%%l"
SET "LINE=###^"!LINE:;=^";^"!^"###"
SET "LINE=!LINE:###^";=^"^";!"
SET "LINE=!LINE:;^"###=;^"^"!"
SET "LINE=!LINE:###=!"
for /F "delims=; tokens=1-7" %%m in ("!LINE!") DO (
echo FUNCGROUP=%%~r a=%%~m b=%%~n c=%%~o d=%%~p e=%%~q f=%%~s
)
)
Unfortunately, you have not provided sufficient information for what you intend to do with your empty field, so this is only a demonstration to provide you with output similar to that which you've indicated in your question:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F "Delims==" %%G In ("(Set Field[) 2>NUL") Do Set "%%G="
Set "i=0"
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("testlist.txt") Do Call :GetFields "%%G"
Pause
GoTo :EOF
:GetFields
Set "Record=%~1"
Set /A i += 1
SetLocal EnableDelayedExpansion
Set "Index=1"
Set "Field[!Index!]=%Record:;=" & Set /A Index +=1 & Set "Field[!Index!]=%"
Echo(&Echo Record !i!
For /L %%G In (1,1,!Index!) Do If Not Defined Field[%%G] (Echo Field[%%G]=) Else Set Field[%%G]
Exit /B

escaping "!" in a for-loop with delayed variable expansion

I need to escape "!" (and other special chars) in a for-loop where delayed variable expansion is enabled
I've tried manually escaping the loop-varible with string substitution ^^! in the loop-variable, %%a but with no luck at all. Is it already too late after the for has read them? If so, how the heck can I even accomplish this?
Below is a short function. The only relevant part here is the for-loop and the echo statement. That is printing out whole lines from a file every X'th line, and those lines are file-paths. They (sometimes) contain characters like "!" and other troublesome special characters. I just want echo here to pass it without interpreting it at all - but instead it ends up deleting my "!" chars.
For my use they need to be exactly correct or they are useless as they must correlate to actual files later on in what I use them for.
setlocal EnableDelayedExpansion
:SplitList
if [%3] == [] goto :SplitListUsage
set inputfile=%~1
set outputfile=%~2
set splitnumber=%~3
set skipLines=0
set skipLines=%~4
if %skipLines% GTR 0 (
set skip=skip=%skipLines%
) else (
set skip=
)
#echo off > %outputfile%
set lineNumber=0
for /f "tokens=* %skip% delims= " %%a in (%inputfile%) do (
set /a modulo="!lineNumber! %% !splitnumber!"
if "!modulo!" equ "0" (
echo %%a >> !outputfile!
)
set /a lineNumber+=1
)
exit /B 0
Quick solution:
if !modulo! equ 0 (
setlocal DisableDelayedExpansion
(echo %%a)>> "%outputfile%"
endlocal
)
Better solution:
Based on your code sample, there is no need to have delayed expansion enabled for the entire code,in fact you should keep it disabled to not mess with the file names or input strings which may contain !, and enabled it when necessary:
setlocal DisableDelayedExpansion
REM Rest of the code...
for /f "tokens=* %skip% delims= " %%a in (%inputfile%) do (
set /a "modulo=lineNumber %% splitnumber"
setlocal EnableDelayedExpansion
for %%m in (!modulo!) do (
endlocal
REM %%m is now have the value of modulo
if %%m equ 0 (
(echo %%a)>> "%outputfile%"
)
)
set /a lineNumber+=1
)
Side notes:
There are some other issues with your code which as you might have noticed, some of them are corrected in the above solutions. But to not distract you from main issue you had, I covered them here separately.
There is also room for improving the performance of the code when writing to the outputfile.
Here is the re-written code which covers the rest:
#echo off
setlocal DisableDelayedExpansion
:SplitList
if "%~3"=="" goto :SplitListUsage
set "inputfile=%~1"
set "outputfile=%~2"
set "splitnumber=%~3"
set "skipLines=0"
set /a "skipLines=%~4 + 0" 2>nul
if %skipLines% GTR 0 (
set "skip=skip=%skipLines%"
) else (
set "skip="
)
set "lineNumber=0"
(
for /f "usebackq tokens=* %skip% delims= " %%a in ("%inputfile%") do (
set /a "modulo=lineNumber %% splitnumber"
setlocal EnableDelayedExpansion
for %%m in (!modulo!) do (
endlocal
REM %%m is now have the value of modulo
if %%m equ 0 echo(%%a
)
set /a lineNumber+=1
)
)>"%outputfile%"
You didn't protect the variable assignments with double qoutes " e.g. set inputfile=%~1. If the now naked batch parameter %~1 contains spaces or special characters like & your batch files fails, either fatally with a syntax error or at execution time with incorrect data. The recommended syntax is to use set "var=value" which does not assign the quotes to the variable value but provides protection against special characters. It also protects the assignment against the accidental trailing spaces.
The %inputfile% may contain special characters or spaces, so it should be protected by double quoting it when using in the FOR /F's IN clause. When double quoting the file name in FOR /F the usebackq parameter must also be used.
With SET /A there is no need to expand the variable values: set /a modulo="!lineNumber! %% !splitnumber!". The variable names can be used directly, and it will work correctly with or without delayed expansion.
Using (echo %%a) >> "%outputfile%" inside a FOR loop introduces a severe performance penalty specially with a large number of iterations, because at each iteration the output file will be opened, written to and then closed. To improve the performance The whole FOR loop can redirected once. Any new data will be appended to the already opened file.
The odd looking echo( is to protect against the empty variable values, or when the variable value is /?. Using echo %%a may print the `ECHO is on/off' message if the variable is empty or may print the echo usage help.
In the main solutions, The reason I've used (echo %%a)>> "%outputfile%" instead of echo %%a >> "%outputfile%" is to prevent outputting the extra space between %%a and >>. Now you know the reason for using echo(, it is easy to understand the safer alternative: (echo(%%a)>> "%outputfile%"

Escaping exclamation marks with delayed expansion

I have a batch file I'm using to search for a piece of text and replace it with another. It works, but what's happening is that when the 'new' file is created, anything after an exclamation mark is deleted.
So original file
Hello! I have some cheese
Just becomes
Hello
Although the text I am trying to replace is fine.
I understand that since I'm using delayed expansion I need to somehow escape the exclamation marks with ^^! but can't figure out where to do this. Adding it at the echo just echoes the ^^! literally.
Any help would be appreciated.
set "rootname=Common Filename"
set "replace="old">"
set "replaced="new">"
set "source="%rootname%_unqiue_suffix.txt""
set "target=Fixed\%SOURCE%"
setlocal enableDelayedExpansion
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
set "line=%%b"
if defined line set "line=!line:%replace%=%replaced%!"
echo(!line!
)
) > %target%
endlocal
To avoid loss of exclamation marks, enable delayed expansion only when it is really needed and expand normal % variables and for meta-variables only when delayed expansion is disabled:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "rootname=Common Filename"
set "replace="old">"
set "replaced="new">"
set "source=%rootname%_unqiue_suffix.txt"
set "target=Fixed\%source%"
(
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "%source%"') do (
set "line=%%b"
setlocal EnableDelayedExpansion
if defined line set "line=!line:%replace%=%replaced%!"
echo(!line!
endlocal
)
) > "%target%"
endlocal
This code still causes trouble in case the variables replace and replaced contain !-signs, because they are percent-expanded.

Batch Counter not working

I've got this code on a batch file:
setlocal EnableDelayedExpansion
set var=0
for /f "tokens=*" %%a in (kim.txt) do (
set /a var+=1
echo Line %var%
)
But %var% don't seem to increment, it's always at zero. Tried different aproaches without any luck.
Under delayed expansion you need to access your variables (in case their value changes within brackets context) with ! instead of % :
setlocal EnableDelayedExpansion
set var=0
for /f "tokens=*" %%a in (kim.txt) do (
set /a var+=1
echo Line !var!
)

Splitting string using delayed expansion limitation

When I was testing a script I came across this issue when trying to extract characters from a string using batch. I have simplified it into a simple example. t.txt just contains the word hello.
#echo off
setlocal enabledelayedexpansion
set a=0
set b=1
for /f %%a in (t.txt) do (
set x=%%a
echo !x:~!a!,!b!!
set /a x+=1
)
pause >nul
The problem is, the variable x needs to be accessed using delayed expansion, and because I am updating the values of a and b through the loop these also need to be accessed using delayed expansion.
When trying to use the variables a and b to split the string they all need delayed expansion, but the order of the ! marks means that it is not parsed the way I intended!
CMD will expand my command as !x:~!, !,! and !!, instead of expanding the inner ones first. Obviously I can't use %'s either.
The only way I have found to get around this is to call an external function that isn't in the loop, so I can use %'s.
#echo off
setlocal enabledelayedexpansion
set a=0
set b=1
set v=
for /f %%a in (t.txt) do (
set x=%%a
call :RETURN x
set /a x+=1
)
pause >nul
:RETURN
set v=%1
echo %v:~!a!,!b!%
Is there any way of getting cmd to parse my command how I need it to, or this just a limitation I will have to use call for?
Simply transfer variables a and b to FOR variables.
#echo off
setlocal enabledelayedexpansion
set a=0
set b=1
for /f %%a in (t.txt) do (
set "x=%%a"
for /f "tokens=1,2" %%A in ("%a% %b%") do echo !x:~%%A,%%B!
REM this line makes no sense if x=hello: set /a x+=1
)
pause >nul
Mixing delayed and normal expansion will work.
#echo off
setlocal EnableDelayedExpansion
set a=0
set b=1
for /f %%L in (t.txt) do (
set "x=%%L"
echo !x:~%A%,%B%!
)

Resources