Why does my for-loop not increase my variable? - for-loop

I program a batch file, but I'm new to for-loops which I need.
I now know how the syntax works, but I cannot figure out why my loop does not do what it should.
This code is an extract from my file:
#echo off & setlocal enabledelayedexpansion
set /p file=:
set /a numberofgoals=0
for /f "delims=" %%a in ("%file%.txt") do set /a "numberofgoals+=1"
echo %numberofgoals%
pause > nul
If I did everything right my output should be the length of the specified textfile and it worked for me before, but apperently I changed something in the code that I'm not sure about and the output of %numberofgoals% is everytime exactly 1 now, regardless of how long my text file is.
My question is: What have I done wrong and why is the output 1 now? I cannot even remember having changed something there...
EDIT: I changed "delims=" into "usebackq delims=" as suggested and it works now, thank you.

The quotes in the for loop's parentheses mean "process this string" rather than "read this file". Use the usebackq option to indicate that the quotes are providing a filename:
for /f "usebackq delims=" %%a in ("%file%.txt") do set /a "numberofgoals+=1"
and you should be golden.
Type help for in your cmd window for the gory details.

Related

Is there a way to loop through a files contents and use it in batch?

I'm working on a project in batch-file and need to read the contents of a file and print out the contents. I have a set up a for /f loop to go through the contents of the file, but it isn't working.
The Code
cd C:\Users\(name)
for /f %%G in (List.txt) (
del /F /S %%G
echo %errorlevel%
pause
)
I have been Googling for about an hour now, and can't find anything which has worked for me. I was hoping that you guys could tell me what I'm doing wrong with my code.
Default delimiters in cmd for the for /F loop is whitespace. Your code will split and assign the first word/number/line up until the first whitespace.
You need to tell the for /f loop to not use any delimiters. Also usebackq in order for you to double quote your file as that can also be a full path to the file with whitespace, i.e: "C:\My Documents\Old Files\list.txt"
#echo off
for /f "usebackq delims=" %%i in ("List.txt") do del /F /S "%%~i"
then, del does not set the errorlevel and you will always get 0 returned. if you really want to check the result, redirect stderr to stdout and use findstr to determine if delete was successful.

Regarding FIND Instruction using Batch File. Storing result in a variable

I am searching a string on a list of files under many subfolders. If the string is found, then i'll perform more statements (which is not important anyways on my question).
So what I did is use the FIND instruction, using /c option. Then store the result of the FIND trimming the right most last character (which is the number of occurence of the string on a file). See below for the sample code...
for /F "delims=" %%A in ('find /c "PROGRAM-ID." "C:\FORTEST\CBL001.cbl"') do SET findstr=%%A
SET findstr=%findstr:~-1%
echo %findstr%
That code above gives me
1
Which is correct because the word "PROGRAM-ID" only occurred once in CBL001.cbl..
Now if I implement this code on a for loop to check for all files within a folder...
for /r "C:\FORTEST\" %%G in (*.*) do (
for /F "delims=" %%A in ('find /c "PROGRAM-ID." %%G') do SET findstr1=%%A
SET findstr1=%findstr1:~-1%
echo %findstr1%
)
The echo is returning
ECHO is off.
I dont understand how inserting the for loop into another for loop would mess up the code....
Thanks for reading.
=======
EDIT: Problem solved. Thanks to #npocmaka
So originally, before all this fiasco regarding storing FIND result into variable, I was using ERRORLEVEL to determine if the FIND instruction found the string or not (which sounds much simpler), but the ERRORLEVEL is not returning the correct value even if the string was not found. Hence my current solution of storing the result of FIND and using that data for comparison.
But it seems DelayedExpansion was the original culprit causing ERRORLEVEL to not produce the correct value.
See below for final code.
for /r "C:\FORTEST" %%G in (*.*) do (
find "PROGRAM-ID." %%G
if !ERRORLEVEL! EQU 0 (
echo ITS A COBOL
::do something
) else (
echo NOPE NOT A COBOL
)
)
Also thanks to other replies, tho I didnt bother using them, but I appreciate the efforts!
find doesn't write the dashes and filenames, if it gets it's input from a pipe. And there is no need for an additional variable (findstr1):
for %%G in (*.*) do (
for /F "delims=" %%A in ('type %%G^|find /c "PROGRAM-ID."') do echo %%A %%G
)
The correct answer is using EnableDelayedExpansion
Thanks to #npocmaka

Get string from file in batch

Task in CMD.
1) How can I compare if string is in string? I checked manual here for "Boolean Test "does string exist ?"" But I can't understand the example or it does not work for me. This piece of code, it is just a try. I try to make a string compare of filter some sting if there is a tag <a> in a line.
FOR /f "tokens=* delims= usebackq" %%c in ("%source%") DO (
echo %%c
IF %%c == "<a" (pause)
)
So while I read a file, it should be paused if there is a link on a line.
2) I have one more ask. I would need to filter the line if there is a specific file in the link, and get content of the link. My original idea was to try to use findstr with regex, but it seems not to use sub-patterns. And next problem would be how to get the result to variable.
set "pdf=0_1_en.pdf"
type "%source%" | grep "%pdf%" | findstr /r /c:"%pdf%.*>(.*).*</a>"
So in summary, I want to go through file and if there is a link like this: REPAIRED: *
<b>GEN 0.1 Preface</b>
I forgot to style this as a code, so the inside of code was not displayed. Sorry.
Warnning: we don't know the path, only the basic filename.
Get the title GEN 0.1 Preface. But you should know, that there are also similar links with same link, which contain image, not a text inside a tag.
Code according Aacini to be changed a little bit:
#echo off
setlocal EnableDelayedExpansion
set "source=GEN 0 GENERAL.html"
set "pdf=0_1_en.pdf"
echo In file:%source%
echo Look for anchor:%pdf%
rem Process each line in %source% file:
for /F "usebackq delims=" %%c in ("%source%") do (
set "line=%%c"
rem Test if the line contain a "tag" that start with "<a" string:
set "tag=!line:*<a=!"
if not "!tag!" == "!line!" (
rem Take the string in tag that end in ">"
for /F "delims=^>" %%a in ("!tag!") do set "link=%%a"
echo Link found: !link!
if "!link!" == "GEN 0.1 Preface" echo Seeked link found
)
)
pause
Still not finished
Although your question is extensive it does not provide to much details, so I assumed several points because I don't know too much about .PDF files, tags, etc.
#echo off
setlocal EnableDelayedExpansion
set "source=GEN 0 GENERAL.html"
set "pdf=0_1_en.pdf"
echo In file: "%source%"
echo Look for anchor: "%pdf%"
rem Process each line in %source% file:
for /F "usebackq delims=" %%c in ("%source%") do (
set "line=%%c"
rem Test if the line contain "<a>" tag:
set "tag=!line:*<a>=!"
if not "!tag!" == "!line!" (
rem Test if "<a>" tag contain the anchor pdf file:
if not "!tag:%pdf%=!" == "!tag!" (
rem Get the value of "<b>" sub-tag
set "tag=!tag:<b>=$!"
set "tag=!tag:</b>=$!"
for /F "tokens=2 delims=$" %%b in ("!tag!") do set title=%%b
echo Title found: "!title!"
)
)
)
pause
Any missing point can be added or fixed, if you give me precise details about them.
EDIT: I fixed the program above after last indications from the OP. I used $ character to get the Title value; if this character may exist in original Tag, it must be changed by another unused one.
I tested this program with this "GEN 0 GENERAL.html" example file:
Line one
<a>href="/Dokumenter/EK_GEN_0_X_en.pdf" class="uline"><b>GEN 0.X Preface</b></a>
Line three
<a>href="/Dokumenter/EK_GEN_0_1_en.pdf" class="uline"><b>GEN 0.1 Preface</b></a>
Line five
and get this result:
In file: "GEN 0 GENERAL.html"
Look for anchor: "0_1_en.pdf"
Title found: "GEN 0.1 Preface"
EDIT: New faster method added
There is a simpler and faster method to solve this problem that, however, may fail if a line contains more than one tag:
#echo off
setlocal EnableDelayedExpansion
set "source=GEN 0 GENERAL.html"
set "pdf=0_1_en.pdf"
echo In file: "%source%"
echo Look for anchor: "%pdf%"
for /F "delims=" %%c in ('findstr /C:"<a>" "%source%" ^| findstr /C:"%pdf%"') do (
set "tag=%%c"
rem Get the value of "<b>" sub-tag
set "tag=!tag:<b>=$!"
set "tag=!tag:</b>=$!"
for /F "tokens=2 delims=$" %%b in ("!tag!") do set title=%%b
echo Title found: "!title!"
)
pause
First, one important question: does this really have to be implemented via a CMD script? Would you be able to go with VBScript, PowerShell, C#, or some other scripting/programming language? CMD is a notoriously painful scripting environment.
Secondly, I'm not sure if this answers your question--it's a bit unclear--but here's a quick trick you can use to see in CMD to see if a given string contains another substring:
setlocal enableextensions enabledelayedexpansion
set PATTERN=somepattern
for /f "delims=" %%f in (somefile.txt) do (
set CURRENT_LINE=%%f
if "!CURRENT_LINE:%PATTERN%=!" neq "!TEMP!" (
echo Found pattern in line: %%f
)
)
The idea is that you try to perform string replacement and see if anything was changed. This is certainly a hack, and it would be preferable if you could instead use a tool like findstr or grep, but if you're limited in your options, something like the above should work.
NOTE: I haven't actually run the above script excerpt, so let me know if you have any difficulty with it.
I have modified the way to do it. I realized that it is better to find name of pdf document first. This is my almost completed solution, but I ask you if you could help me with the last point. The last replacing statement does not work because I need to remove closing tag b. Just to get the title.
#echo off
setlocal EnableDelayedExpansion
set "source=GEN 0 GENERAL.html"
set "pdf=0_1_en.pdf"
echo In file:%source%
echo Look for anchor:%pdf%
rem Process each line in %source% file:
for /F "usebackq delims=" %%c in ("%source%") do (
set "line=%%c"
REM Test if the line contains pdf file I look for:
SET "pdfline=!line:%pdf%=!"
if not "!pdfline!" == "!line!" (
cls
echo Line: !line!
REM Test if the pdfline contains tag b
SET "tagline=!pdfline:*><b>=!"
if not "!tagline!" == "!pdfline!" (
cls
echo ACTUAL LINE: !tagline!
REM Remove closing tag b
SET "title=!tagline:</b*=!"
echo TITLE: !title!
pause
)
)
)
pause
BTW:
The html page I work with is here.
So I ask you to help complete/repair line SET "title=!tagline:</b*=!"

Batch Script issue

for deleting files, I will be using the code below to remove the oldest file in the directory and run it every day. It came from the question of mine.
Applying to the original batch script:
SET BACKUPDIR=C:\PATH\TO\BACKUPS
FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
DEL %BACKUPDIR%\%OLDEST%
Something such as that checks if the file amount is 21, if so delete the latest one:
SET BACKUPDIR=C:\test
SET countfiles = dir BACKUPDIR /b | find /v /c "::"
if countfiles > 21
FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
DEL %BACKUPDIR%\%OLDEST%
EDIT: Sorry for forgetting the question, my attempt was failing, I would be greatful for any way to direct how to make it work.
first, it seems set does not like spaces between the variable and the = sign: if you put a space, the variable name will include a space. so you must remove the space to properly define the variable name.
plus, your syntax for capturing the output of the command into a variable is wrong. the only way i am aware of (after desperately searching stackoverflow for the answer) is to use a for loop trick to use a temporary variable (see this question for more details). actually, you also need to escape the pipe for the command to be parsed correctly.
then, when the variable tested in the if expression does not exists, the results is always true, so make sure the variable exists. by removing the space as said above, the name in the if expression will match your variable name, and the test will execute properly.
then you forgot to make a block around the 2 last commands. actually, you are testing if you have more than 21 files and compute the oldest if it is true, then you ALWAYS delete the oldest.
also, the greater than operator > may be understood as a redirection. you may need to use the GTR operator.
SET BACKUPDIR=C:\test
FOR /F %%i in ('dir BACKUPDIR /b ^| find /v /c "::"') DO SET countfiles=%%i
if countfiles GTR 21 (
FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
DEL %BACKUPDIR%\%OLDEST%
)
That's not working...you can't set 'normal' variables within a for-loop. I had the same problem some days ago and solved it with this blog entry.
Basically, you need to set SETLOCAL ENABLEDELAYEDEXPANSION and then use ! instead of %...
set FILES=
for /f %%a IN (‘dir /b *.txt’) do set FILES=!FILES! %%a
echo %FILES%
So, this should work for you:
SETLOCAL ENABLEDELAYEDEXPANSION
SET OLDEST=
FOR /F %%i IN ('DIR /B /O-D %BACKUPDIR%') DO SET OLDEST=%%i
DEL %BACKUPDIR%\%OLDEST%

How can I load the contents of a text file into a batch file variable?

I need to be able to load the entire contents of a text file and load it into a variable for further processing.
How can I do that?
Here's what I did thanks to Roman Odaisky's answer.
SetLocal EnableDelayedExpansion
set content=
for /F "delims=" %%i in (test.txt) do set content=!content! %%i
echo %content%
EndLocal
If your set command supports the /p switch, then you can pipe input that way.
set /p VAR1=<test.txt
set /? |find "/P"
The /P switch allows you to set the value of a variable to a line of
input entered by the user. Displays the specified promptString before
reading the line of input. The promptString can be empty.
This has the added benefit of working for un-registered file types (which the accepted answer does not).
Use for, something along the lines of:
set content=
for /f "delims=" %%i in ('filename') do set content=%content% %%i
Maybe you’ll have to do setlocal enabledelayedexpansion and/or use !content! rather than %content%. I can’t test, as I don’t have any MS Windows nearby (and I wish you the same :-).
The best batch-file-black-magic-reference I know of is at http://www.rsdn.ru/article/winshell/batanyca.xml. If you don’t know Russian, you still could make some use of the code snippets provided.
You can use:
set content=
for /f "delims=" %%i in ('type text.txt') do set content=!content! %%i
Can you define further processing?
You can use a for loop to almost do this, but there's no easy way to insert CR/LF into an environment variable, so you'll have everything in one line. (you may be able to work around this depending on what you need to do.)
You're also limited to less than about 8k text files this way. (You can't create a single env var bigger than around 8k.)
Bill's suggestion of a for loop is probably what you need. You process the file one line at a time:
(use %i at a command line %%i in a batch file)
for /f "tokens=1 delims=" %%i in (file.txt) do echo %%i
more advanced:
for /f "tokens=1 delims=" %%i in (file.txt) do call :part2 %%i
goto :fin
:part2
echo %1
::do further processing here
goto :eof
:fin
To read in an entire multi-line file but retain newlines, you must reinsert them. The following (with '<...>' replaced with a path to my file) did the trick:
#echo OFF
SETLOCAL EnableDelayedExpansion
set N=^
REM These two empty lines are required
set CONTENT=
set FILE=<...>
for /f "delims=" %%x in ('type %FILE%') do set "CONTENT=!CONTENT!%%x!N!"
echo !CONTENT!
ENDLOCAL
You would likely want to do something else rather than echo the file contents.
Note that there is likely a limit to the amount of data that can be read this way so your mileage may vary.
Create a file called "SetFile.bat" that contains the following line with no carriage return at the end of it...
set FileContents=
Then in your batch file do something like this...
#echo off
copy SetFile.bat + %1 $tmp$.bat > nul
call $tmp$.bat
del $tmp$.bat
%1 is the name of your input file and %FileContents% will contain the contents of the input file after the call. This will only work on a one line file though (i.e. a file containing no carriage returns). You could strip out/replace carriage returns from the file before calling the %tmp%.bat if needed.
for /f "delims=" %%i in (count.txt) do set c=%%i
echo %c%
pause

Resources