how to do loop in Batch? - windows

I want to create something like this
dup.bat infile outfile times
example usage would be
dup.bat a.txt a5.txt 5
at it would create file a5.txt that has the content of a.txt repeated 5 times
however I do not know how to do for loop in batch, how to do it?

You can do the loop like this:
SET infile=%1
SET outfile=%2
SET times=%3
FOR /L %%i IN (1,1,%times%) DO (
REM do what you need here
ECHO %infile%
ECHO %outfile%
)
Then to take the input file and repeat it, you could use MORE with redirection to append the contents of the input file to the output file. Note this assumes these are text files.
#ECHO off
SET infile=%1
SET outfile=%2
SET times=%3
IF EXIST %outfile% DEL %outfile%
FOR /L %%i IN (1,1,%times%) DO (
MORE %infile% >> %outfile%
)

For command line args
set input=%1
set output=%2
set times=%3
To do a simple for loop, read in from the input file, and write to the output file:
FOR /L %%i IN (1,1,%times%) DO (
FOR /F %%j IN (%input%) DO (
#echo %%j >> %output%
)
)
Instead of taking in an output file, you could also do it via command line:
dup.bat a.txt 5 > a5.txt

sigh
Compact Design:
SETLOCAL ENABLEDELAYEDEXPANSION
SET times=5
:Beginning
IF %times% NEQ 0 (TYPE a.txt>>a5.txt & SET /a times=%times%-1 & GOTO Beginning) ELSE ( ENDLOCAL & set times= & GOTO:eof)
Easy Reading:
SETLOCAL ENABLEDELAYEDEXPANSION
SET times=5
:Beginning
IF %times% NEQ 0 (
TYPE a.txt>>a5.txt
SET /a times=%times%-1
GOTO Beginning
) ELSE (
ENDLOCAL
set times=
GOTO:eof
)
Set your counter (times=5)
Start subroutine Beginning
If your counter doesn't equal 0, read a.txt and APPEND its contents to a5.txt, then decrement your counter by 1. This will repeat five times until your counter equals 0, then it will cleanup your variable and end the script.
SET ENABLEDELAYEDEXPANSION is important to increment variables within loops.

Related

Batch - Get block of text between flags, output and iterate over all files

I've got a bunch of text files in a directory that have a block of text I want to extract between two strings into a new text file of a similar name. I've got the single file working but think I've come unstuck with looping through all .txt files. Maybe at the "goto" command?
Here is the original, single file code I used:
Batch File - Find two lines then copy everything between those lines
~Top Break
foobar
~ more data title
more foobar
~Bottom Break
Garbage data
I have this code that works for a single file called FileNumber1.txt.
#echo off
set "FIRSTLINE=~Top Break"
set "LASTLINE=~Bottom Break"
set "INFILE=FileNumber1.txt"
setlocal EnableExtensions DisableDelayedExpansion
set "FLAG="
> "%INFILE%_MyData.txt" (
rem findstr configured so that each line in a file is given a "1:" number and colon.
for /F "delims=" %%L in ('findstr /N "^" "%INFILE%"') do (
set "LINE=%%L"
setlocal EnableDelayedExpansion
rem this LINE=!LINE:*:=! removes the any character before the Colon. *:
set "LINE=!LINE:*:=!"
rem this block of code checks to see if line of text = Firstline variable, if so FLAG = TRUE
if "!LINE!"=="%FIRSTLINE%" (
endlocal
set "FLAG=TRUE"
rem this block of code checks to see if line of text = Lastline variable, if so goto :Continue and end the loop
) else if "!LINE!"=="%LASTLINE%" (
endlocal
goto :CONTINUE
) else if defined FLAG (
echo(#!LINE!
endlocal
) else (
endlocal
)
)
)
:CONTINUE
endlocal
NewFile1_MyData.txt Output:
foobar
~ more data title
more foobar
I've tried to wrap this in another "FOR" loop that looks for all txt files in the same directory.
This is my code that isn't working.
#echo off
set "FIRSTLINE=~Top Break"
set "LASTLINE=~Bottom Break"
for /F %%f in (*.txt) do (
set "INFILE=%%f"
setlocal EnableExtensions DisableDelayedExpansion
set "FLAG="
> "%INFILE%_OldHeader.txt" (
rem findstr configured so that each line in a file is given a "1:" number and colon.
for /F "delims=" %%L in ('findstr /N "^" "%INFILE%"') do (
set "LINE=%%L"
setlocal EnableDelayedExpansion
rem this LINE=!LINE:*:=! removes the any character before the Colon. *:
set "LINE=!LINE:*:=!"
rem this block of code checks to see if line of text = Firstline variable, if so FLAG = TRUE
if "!LINE!"=="%FIRSTLINE%" (
endlocal
set "FLAG=TRUE"
rem this block of code checks to see if line of text = Lastline variable, if so goto :Continue and end the loop
) else if "!LINE!"=="%LASTLINE%" (
endlocal
goto :CONTINUE
) else if defined FLAG (
echo(#!LINE!
endlocal
) else (
endlocal
)
)
)
endlocal
:CONTINUE
))
The Command window gets to the "for /F" statement and exits.
Mmm... I would change the method to extract the lines for a simpler one based on lines to skip at beginning of file and number of lines to extract. After that, I would use a for to process all files and call a subroutine to extract the lines:
#echo off
setlocal EnableDelayedExpansion
set "FirstLine=~Top Break"
set "LastLine=~Bottom Break"
rem Process all text files in this folder
for %%f in (*.txt) do (
rem Search for First line and Number of lines
set "FirstNum="
for /F "delims=:" %%n in ('findstr /C:"%FirstLine%" /C:"%LastLine%" /N "%%f"') do (
if not defined FirstNum (
set "FirstNum=%%n"
) else (
set /A "LastNum=%%n-FirstNum-1"
)
)
rem Copy the lines
call :CopyLines >"%%~Nf_MyData.out" "%%f", !FirstNum!, !LastNum!
)
ren *.out *.txt
goto :EOF
:CopyLines File, Skip, Num
set "Num=%3"
for /F "usebackq skip=%2 delims=" %%a in (%1) do (
setlocal DisableDelayedExpansion
echo %%a
endlocal
set /A Num-=1
if !Num! equ 0 exit /B
)
exit /B

CMD - Pivot / Transpose lines of file [e.g. convert 1500 lines (x1 col) --> 500 lines (x3 col)]

how please can I 'pivot' or transpose a file (i.e. turn a single-column list, into a table of data)...
Currently...
VideoA.name
VideoA.size
VideoA.bitrate
VideoB.name
VideoB.size
VideoB.bitrate
...
Desired...
VideoA.name, VideoA.size, VideoA.bitrate
VideoB.name, VideoB.size, VideoB.bitrate
Name
Size
Bitrate
VideoA.name
VideoA.size
VideoA.bitrate
VideoB.name
VideoB.size
VideoB.bitrate
Extra Info / Context
I'm aware people often ask 'why are you doing this?' so (if interested), here is the wider context / problem I am trying to solve...
I have a list of files in Files.txt
I have a jscript batch file getProps.bat that extract file properties and prints them, 1 per line
I have written a batch file to loop through Files.txt, get the properties of each and write the output to Details.csv
However if I have 500 files x 3 properties, this currently gives me 1500 lines, and I want 500 lines x 3 columns
GetProps_AllFiles.bat
---------------------
#ECHO OFF
SETLOCAL
FOR /F "tokens=*" %%A in (Files.txt) do (
getprops %%A 0,1,320 /noheaders >> Details.csv
)
Thanks in advance!
Use the "standard way" (for /f) to read a file line by line, extended by a counter. Add the line to a string (line), followed by a comma (or whatever you want to use as separator), and increase the counter. Except it's the third line. Then print the string plus the current line, reset the counter and string, and repeat.
#echo off
setlocal enabledelayedexpansion
set "line="
set count=0
(for /f "delims=" %%a in (test.txt) do (
set /a count+=1
if !count! equ 3 (
echo !line!%%a
set "line="
set count=0
) else (
set line=!line!%%a,
)
))>test.csv
The below is a slight adjustment to the code kindly provided by Stephan that allows a filename and number of lines to be passed into the script...
ColumiseFile.cmd
----------------
#ECHO OFF
SETLOCAL enabledelayedexpansion
REM :: USAGE -----------------------------------------------------------------
REM ColumiseFile [1]File.txt [2]NumOfLines
REM > Concatenates every n Lines into 1, exporting result to File.csv
SET "File=%1"
SET /A Lines=%2
REM :: MAIN -------------------------------------------------------------------
REM Thanks to Stephan [https://stackoverflow.com/a/67664755/15919675]
REM Loops through input file, compacting every n lines into 1
set "line="
set count=0
(for /f "delims=" %%a in (%File%) do (
set /a count+=1
if !count! equ %Lines% (
echo !line!%%a
set "line="
set count=0
) else (
set line=!line!%%a,
)
REM :: OUTPUT -----------------------------------------------------------------
REM Create .csv in same location as source .txt file
))>%~dpn1.csv

Batch code to turn every line of text to a variable

So I need a code that will take a text file (we'll cal it list.txt) and turn every line into a variable.
To give some context, I have some file names listed in list.txt, which adds and deletes file names occasionally by user request. I want the user of this code to be able to select which document they'd like to open using variables.
For example, if list.txt looks like this
list.txt
loading.txt
test1.txt
test2.txt
test3.txt
test4.txt
Then I'd like an automatic variable for every .txt listed. I then would add a simple if statement to open the file matched with the variable.
Am I making this too complicated for myself or is this the only way to do this?
EDIT:
I am not attempting something like this:
type list.txt
echo.
echo.
set /p GROUPSELECT= Please type out the FULL name of the group listed:
CD %grouplist%
type %GROUPSELECT%
It will display the file contents, and then display the specific file chosen by the input. I'd think that the variable might be easier to do more with later though, just a thought.
Edit2
I tried this:
#Echo OFF
FOR /F "Usebackq Delims=" %%a IN (
"list.txt"
) DO (
set jim=%%a
)
echo %jim%
PAUSE
%jim% will only be the last line in the text file, so how do I make the next step into making them all different?
Give this a try. I have commented each line of code that should explain what it is doing. Let me know if you need any further explanation.
#echo off
REM FOR commnd is parsing the output of the FINDSTR commands.
REM First FINDSTR command is finding non empty lines in the file
REM Second Findstr command is assigning a number to each line of the file
REM The output is split into two tokens and the number is assigned to %%G and the line of the file to %%H
for /F "tokens=1* delims=:" %%G IN ('findstr /V "^$" list.txt ^|findstr /N ".*"') DO (
REM create a variable of the lines in the file
set "file%%G=%%H"
REM Create a menu of the lines in the file
echo %%G %%H
REM Get the number of lines in the output
set "num=%%G"
)
REM Ask user to chose a number
set /P "filenum=Chose a file number 1 to %num%:"
REM Need to enable delayed expansion to use array variables
setlocal enabledelayedexpansion
REM Check if they type in a correct number and display the file
IF DEFINED file%filenum% type !file%filenum%!
endlocal
pause
Is this the sort of thing you're looking for?
#For /F "Delims==" %%# In ('Set # 2^>Nul')Do #Set "%%#="
#For /F Tokens^=1*Delims^=[]^ EOL^= %%# In ('Type "file.txt"^|Find /V /N ""'
)Do #Set "#%%#=%%$"&Echo( %%#. %%$
:Opt
#Echo(
#Set /P "Opt=Choose a document to open>"
#Set #|Findstr /B "#%Opt%=">Nul||GoTo :Opt
#Call Start "" "%%#%Opt%%%"
Just change the name of the text file containing your list on line 2 as needed.
#TheBoy your question is confusingly framed. If you DO NOT want the example you put in the edit, then can you better explain what you DO want??
Do you want to say iterate over the list file, and create a choice screen?
#(SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "_ChoiceList_File=C:\Admin\ChoiceFile.txt"
REM Full Character List to populate Choices
SET "_CharList=0 1 2 3 4 5 6 7 8 9 A B C D E F N X"
)
CALL :Main
( ENDLOCAL
EXIT /B 0
)
:Main
REM Now we can Do Our Choices btu lets do it in a Sub Function.
CALL :MakeChoice _ChoiceResult
ECHO.
ECHO The Index Chosen Was: %_Chosen%
ECHO The Result Matched is: "%_ChoiceResult%"
REM ECHO Here you output "%_ChoiceResult%"
TYPE "%_ChoiceResult%"
GOTO :EOF
:MakeChoice
cls
color 1A
SET %~1="
SET "_Choices="
SET "_Chosen="
SET "_Amount="
SET "_Choice.17.Value=Next Set!"
SET "_Choice.18.Value=EXIT!"
SET "_Choice_Show_Next="
echo. Pick a File:
echo.========================
REM Create Numbered Array of Choices and output Choices to the Screen
FOR /F "tokens=* usebackq" %%A IN ("%_ChoiceList_File%") DO (
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=%%A"
IF !_Amount! EQU 16 (
SET /A "_Amount+=2"
CALL :MakeChoice "%~1"
IF DEFINED _Chosen (
IF !_Chosen! NEQ 17 (
REM IF !_Chosen! NEQ 18 (
GOTO :EOF
REM )
)
SET "_Amount="
SET "_Chosen="
)
)
)
IF NOT DEFINED _Chosen (
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=!"
SET /A "_Amount+=1"
SET "_Choice.!_Amount!.Value=EXIT!"
CALL :MakeChoice "%~1"
)
GOTO :EOF
:MakeChoice
CLS
ECHO.
SET "_Temp="
SET "_Choices="
SET /A "_Skipline= !_Amount! - 1"
REM Create Choice List to Display only the choices needed.
FOR %%A IN (%_CharList%) DO (
SET /A "_Temp+=1"
IF !_Temp! LEQ !_Amount! (
IF !_Temp! EQU !_Skipline! (
ECHO.
ECHO.=============================
)
IF DEFINED _Choice.!_Temp!.Value (
SET "_Choices=!_Choices!%%A"
CALL ECHO. %%A : %%_Choice.!_Temp!.Value%%
)
)
)
ECHO.
CHOICE /C !_Choices! /N /M "What File do you want to choose? "
SET "_Chosen=%ERRORLEVEL%"
SET "%~1=!_Choice.%_Chosen%.Value!"
GOTO :EOF
)
You can try this:
#echo off
setlocal enabledelayedexpansion
set jim=
for /f "delims=] tokens=1*" %%a in ('find /v /n "" ^<list.txt') do (
set jim=!jim! %%b
)
echo Select a file from !jim!
set /p file=
type !file!
pause
This will read all lines from the list.txt and return them within a variable !jim!.

Batch Script Iterative form

I have a batch script which :
check the files in a directory and check if it exists in another
directory and it should not exists there
count each file with a specific format, there should be just one of each
if both of the above statement is true then generate a success file.
Below is my code which is working fine:
SET /A file1=file2=Val=0
SET /A FileE=1
set /a flagname=1
for %%A in (*ABC*.txt) do set /a file1+=1
for %%A in (*XYZ*.txt) do set /a file2+=1
for %%i in ("*") do if exist "Processed\%%~nxi" SET /A FileE=0
SET /A Val=%file1%*%file2%*%FileE%
if %Val% EQU 1 (
echo SUCESS>Sucess.txt
SET Flag=Sucess
echo %Flag%) else (
if %file1% EQU 0 ( echo Missing ABC.txt files >> Error.txt)
if %file1% GTR 1 ( echo More than 1 ABC.txt files >> Error.txt)
if %file2% EQU 0 ( echo Missing XYZ.txt files >> Error.txt)
if %file2% GTR 1 ( echo More than 1 XYZ.txt files >> Error.txt)
(for %%i in ("*") do if exist "Processed\%%~nxi" echo(File Exists in
Processed
Folder %%~i)>>Error.txt
SET Flag=FAILURE
echo %Flag%)
My problem is how to transform above code to iterate over a list of number of files like 100 ? Below is the code which I tried :
#echo off
setlocal enable delayed expansion
Set Filen[0]=ABC*.txt
Set Filen[1]=XYZ*.txt
SET /A Val=1
SET /A File1=0
FOR /l %%G in (0,1,1) Do (
echo !Filen[%%G]! hi
set File1=0
echo %file1% Count
for %%A in (!Filen[%%G]! ) do (
set File1=!File1!+1
echo %%A %file1%)
)
Put your search words in a string and iterate over it:
Set "Search=ABC DEF XYZ"
For %%A in (%Search%) do (
Or in a file and read one by one
For /f "usebackq" %%A in ("Search.txt") Do (
With this file Search.txt
ABC
DEF
XYZ
In the environment
> tree a:\ /F
Auflistung der Ordnerpfade für Volume RamDisk
A:\
└───Test
│ ABC_123.txt
│ DEF_456.txt
│ Search.txt
│
└───Processed
The following batch
:: Q:\Test\2018\07\01\SO_51120147.cmd
#Echo off & Setlocal EnableDelayedExpansion
Set "BaseDir=A:\Test"
Set Err=^>^> "Error.txt" Call Echo=[%%date%% %%time%%]
PushD "%BaseDir%" || (%Err% can't locate %BaseDir% & Pause &Goto :Eof)
%Err% Job %~f0 started by %USERNAME%
Set " Flag=Sucess"
:: Uncomment for string variant
:: Set "Search=ABC DEF XYZ"
:: For %%A in (%Search%) do (
:: use the file variant
For /f "usebackq" %%A in ("Search.txt") Do (
Set Cnt=0
For %%B in (%%A*.txt) Do Set /A Cnt=1
if !Cnt! NEQ 1 (
%Err% Count of %%A*.txt file=[!Cnt!]
SET Flag=FAILURE
)
)
For %%A in (*) do if exist "Processed\%%~nxA" (
%Err% File %%A does exist in Processed Folder
Set Flag=FAILURE
)
%Err% Job %~f0 terminated with %Flag%
Yields this output:
> type A:\Test\Error.txt
[2018-07-01 13:10:34,43] Job Q:\Test\2018\07\01\SO_51120147.cmd started by LotPings
[2018-07-01 13:10:34,45] Count of XYZ*.txt file=[0]
[2018-07-01 13:10:34,47] Job Q:\Test\2018\07\01\SO_51120147.cmd terminated with FAILURE
EDIT: Explanation of the somehow tricky line:
Set Err=^>^> "Error.txt" Call Echo=[%%date%% %%time%%]
To not have to pre/append every line which should go to the error log with
the redirection and a (fixed) file name I put these together with the
echo command and a [date time] stamp into a variable.
To avoid immediate interpretation when setting the variable, the '>' have to
be esaped with a caret, and to delay the expansion of the %-signs these have
to be doubled. (otherwise each log entry had the same date time)
To force expansion of date time when echoing the (pseudo) call is neccessary.

Help in writing a batch script to parse CSV file and output a text file

I am struggling to write a batch script which can read a CSV file such as below
Name:, City:, Country:
Mark, London, UK
Ben, Paris, France
Tom, Athens, Greece
There will be a heading row in the CSV file. It should output to a text file as below:
Name:Mark
City:London
Country:UK
Name:Ben
City:Paris
Country:France
Name:Tom
City:Athens
Country:Greece
The field separator (:) in the above output is expected to be provided in the header row itself. So all that I need to do is concatenate the field heading and its value.
The number of columns in this CSV file is not fixed, so the script should not limit to 3 tokens. Kindly help!
#ECHO OFF
IF "%~1"=="" GOTO :EOF
SET "filename=%~1"
SET fcount=0
SET linenum=0
FOR /F "usebackq tokens=1-10 delims=," %%a IN ("%filename%") DO ^
CALL :process "%%a" "%%b" "%%c" "%%d" "%%e" "%%f" "%%g" "%%h" "%%i" "%%j"
GOTO :EOF
:trim
SET "tmp=%~1"
:trimlead
IF NOT "%tmp:~0,1%"==" " GOTO :EOF
SET "tmp=%tmp:~1%"
GOTO trimlead
:process
SET /A linenum+=1
IF "%linenum%"=="1" GOTO picknames
SET ind=0
:display
IF "%fcount%"=="%ind%" (ECHO.&GOTO :EOF)
SET /A ind+=1
CALL :trim %1
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO !f%ind%!!tmp!
ENDLOCAL
SHIFT
GOTO display
:picknames
IF %1=="" GOTO :EOF
CALL :trim %1
SET /a fcount+=1
SET "f%fcount%=%tmp%"
SHIFT
GOTO picknames
This batch scipt:
accepts one parameter, the name of the file to process;
does not verify the presence of : at the end of a header token, and when the values are displayed they are placed immediately after the corresponding header tokens;
trims all the leading spaces (but not the trailing ones);
considers the first row to be the header row, which also defines the number of tokens to process in subsequent rows;
supports up to 10 tokens, and the two areas highlighted in bold italics are responsible for that (so when you need to change the maximum number, modify both areas: if you increase the number, you must expand the "%%a" "%%b" "%%c" … list, and, likewise, if you decrease the number, then shrink the list).
I know this is an old question, but this type of question is my favorite one so here it is my answer:
#echo off
setlocal EnableDelayedExpansion
rem Create heading array:
set /P headingRow=< %1
set i=0
for %%h in (%headingRow%) do (
set /A i+=1
set heading[!i!]=%%~h
)
rem Process the file:
call :ProcessFile < %1
exit /B
:ProcessFile
set /P line=
:nextLine
set line=:EOF
set /P line=
if "!line!" == ":EOF" goto :EOF
set i=0
for %%e in (%line%) do (
set /A i+=1
for %%i in (!i!) do echo !heading[%%i]!%%~e
)
goto nextLine
exit /B
This program have not any limit in the number of fields. This version requires to enclose in quotes the elements that may have spaces or other Batch delimiters, but this restriction may be easily fixed.
Python makes this so easy it should be regulated by the government.
from csv import DictReader
with open('file', 'rb') as file:
reader = DictReader(file)
for line in reader:
for field in reader.fieldnames:
print '{0}{1}'.format(field.strip(), line[field].strip())
print '\n'
Edit: I guess you need something native to the Windows command shell. Oh well.

Resources