How to populate undetermined number of variables from text file? - batch-file

So I know how to save strings to a text file as a list from batch by using
set /p Myvar1="Enter your variable name/value: "
set /p Myvar2="Enter your variable name/value: "
set /p Myvar3="Enter your variable name/value: "
And then append to a text file
echo %Myvar1% %Myvar2% %Myvar3% >> Mylist.txt
So when I open the text file through the batch file, it can be displayed
as a list:
SET "variables="
ECHO =================================================
ECHO My list of stuff
ECHO =================================================
< Mylist.txt (
set /p MyVar1=
set /p MyVar2=
set /p MyVar3=
)
::set line
ECHO - [0] - %Myvar1%
ECHO - [1] - %Myvar1%
ECHO - [2] - %Myvar1%
Now the problem is that:
For each new line on the Mylist.txt text file I have to manually add lines on the batch file. On the provided example the batch is setup so it displays 3 lines of text from the text file. If the text file has 10 lines, it will only show the first 3 lines because that is what is specified. So I would like to accomplish the opposite of this script.
The batch file should be able to:
Batch file reads Mylist.txt.
For each line in Mylist.txt file the batch file creates a "numerated variable".
Each "numerated variable" can be addressable so the user can be prompted to select one of the options on the list 1, 2, 3, 4, etc.

The easiest method to save variable names and their values to a file is redirecting the output of command SET to a file.
set My >"%APPDATA%\MyVariables.txt"
This simple line saves all environment variables starting with My in name with name and value into file MyVariables.txt in application data directory of current user.
And following command line can be used in a batch file to reload those environment variables from file:
for /F "usebackq delims=" %%I in ("%APPDATA%\MyVariables.txt") do set "%%I"
Same command line for usage directly in a command prompt window:
for /F "usebackq delims=" %I in ("%APPDATA%\MyVariables.txt") do set "%I"
A little bit more secure would be checking if each line in the file MyVariables.txt really starts with My as expected and so was not manipulated by someone who wants to override for example PATH by adding manually to file MyVariables.txt a line starting with PATH=.
It might be useful to know how many variables were loaded from file. This can be achieved with a simple extension:
set "VariablesCount=0"
for /F "usebackq delims=" %%I in ("%APPDATA%\MyVariables.txt") do set "%%I" & set /A VariablesCount+=1
Of course if there is an environment variable MyVariablesCount starting with My then this variable with its value would be also saved into the file and reloaded from file which would make it unnecessary to count the number of variables set from file.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
for /?
set /?
Read also the Microsoft article about Using Command Redirection Operators.

simple thing with a for loop plus a counter:
setlocal enabledelayedexpansion
set i=0
for /f "delims=" %%a in (mylist.txt) do (
set /a i+=1
set var[!i!]=%%a
)
echo number of variables: %i%:
set var[
echo ---------
for /l %%a in (1,1,%i%) do echo variable %%a = %var[%%a]%

Related

How can I output each variable from a FOR loop to it's own file

I'm trying to run a query with a FOR loop and have each resulting variable line of the query output into it's own file.
I can't seem to get the delayed expansion variables to behave the way I want them to.
Basically I'm querying the printers installed on a print server and I want a .bat job containing certain text with the result output to multiple files containing the result name.
I think the problem is I'm not escaping some characters correctly, or I'm missing a % or ! on a variable, or some combination thereof.
#echo off
setlocal enabledelayedexpansion
FOR /F "tokens=1 delims=*" %%G IN ('wmic /node:PRINTSERVER printer get name') DO (
SET printer=%%G
SET printers=!printers: =!
SET printers=!printers:Name=!
ECHO ^(
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
ECHO ^"%windir%\system32\cscript %windir%\system32\prnmngr.vbs -ac -p \\PRINTSERVER\!printer!"^)>>!printer!.bat
)
endlocal
Expected results should be multiple files named PRINTERSHARENAME.bat
Each which contains:
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
%windir%\system32\cscript %windir%\system32\prnmngr.vbs -ac -p "\\PRINTSERVER\PRINTERSHARENAME"
EDIT
I will share more of my code. the wmic output contains spaces that had to be stripped, so this is why I used enabledelayedexpansion
EDIT2
Here is the output of my wmic command (Note that there are trailing spaces I've stripped out in the above code and the word 'Name' and a blank line at the end of the command):
C:\Users\bleepbloop>wmic /node:PRNTSVR printer get name
Name
PRINTER1
PRINTER2
OFFICEPRINTER
EDIT3
OK, I'm getting close. Here is code to reproduce, using an answer below:
(
echo Here is my first line
echo Here is my second line
echo Here is my third line
)>"textfile.txt"
FOR /F "delims=" %%G IN ('TYPE textfile.txt') DO (
(
ECHO #ECHO OFF
ECHO ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO ECHO.
ECHO "%%windir%%\system32\cscript" "%%windir%%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\%%G
)>%%G.bat
)
This works as expected and gives me 3 files named
Here is my first line.bat
Here is my second line.bat
Here is my third line.bat
however now I want to strip out all spaces from the variables output by textfile.txt, and for that I think I need to use delayed expansion?
So I want:
Hereismyfirstline.bat
Hereismysecondline.bat
Hereismythirdline.bat
I think I need to do this by using enabledelayedexpansion and inserting the following in the FOR loop:
SET variable=%%G
SET variable=!variable: =!
and then I have to insert the variable back into the loop properly. Still not sure how.
I want the file
Hereismyfirstline.bat
to contain
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
"%windir%\system32\cscript" "%windir%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\Hereismyfirstline
and the next file
Hereismysecondline.bat
to contain:
#ECHO OFF
ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO.
"%windir%\system32\cscript" "%windir%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\Hereismysecondline
I suggest this batch code for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=1 eol=| tokens=*" %%I in ('%SystemRoot%\System32\wbem\wmic.exe /node:PRINTSERVER printer get name 2^>nul') do (
for /F "eol=| delims= " %%J in ("%%I") do (
set Printer=%%~nxJ
if defined Printer (
echo #echo off
echo echo PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
echo echo/
echo "%%SystemRoot%%\system32\cscript.exe" "%%SystemRoot%%\system32\prnmngr.vbs" -ac -p "\\PRINTSERVER\%%~nxJ"
)>"%%~nxJ.bat"
)
)
endlocal
Note: After delims= on second for command line must be a horizontal tab character and not one or more spaces as displayed by the web browsers according to HTML standard for the tab character.
Processing output of wmic is problematic because of this application outputs data always Unicode encoded using UTF-16 Little Endian encoding and for has a quirks on processing this Unicode output correct. For more details see for example How to correct variable overwriting misbehavior when parsing output?
The outer for processes the output of wmic with skipping first line containing heading Name. For all other non-empty lines all leading normal spaces and horizontal tabs are removed before assigning the rest of the line to specified loop variable I even on starting very unusual with a semicolon. | is not allowed in a printer name, but ; would be allowed although having never seen a printer name starting with ;.
The inner for with horizontal tab character as delimiter processes the printer name which can contain one or more spaces, but not a tab character. The printer name with all trailing horizontal tabs removed is assigned to specified loop variable J.
The remaining string is the printer name with trailing normal spaces. Windows prevents usually the creation of a file with trailing spaces. For that reason is assigned to environment variable Printer with using %%~nxJ just the printer name without trailing spaces. But spaces inside the printer name are kept by this command.
A single carriage return caused by the for quirks on processing of output of wmic results in getting environment variable Printer deleted instead of being defined with the carriage return.
So if the environment variable Printer is really defined with a printer name containing no, one or even more spaces, but no leading spaces/tabs and no trailing spaces/tabs, the batch file can be created with using printer name as assigned to loop variable J in batch file and in batch file name using %%~nxJ.
So there is no delayed environment variable expansion needed which makes this batch file working also for a printer name containing an exclamation mark.
See also DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ for the reason writing into the created batch files echo/ instead of echo..
This batch code uses delayed expansion inside the inner loop to remove all spaces from printer name for the batch file name.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "skip=1 eol=| tokens=*" %%I in ('%SystemRoot%\System32\wbem\wmic.exe /node:PRINTSERVER printer get name 2^>nul') do (
for /F "eol=| delims= " %%J in ("%%I") do (
set "Printer=%%~nxJ"
if defined Printer (
setlocal EnableDelayedExpansion
set "BatchFileName=!Printer: =!"
(
echo #echo off
echo echo PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
echo echo/
echo "%%SystemRoot%%\system32\cscript.exe" "%%SystemRoot%%\system32\prnmngr.vbs" -ac -p "\\PRINTSERVER\!Printer!"
)>"!BatchFileName!.bat"
endlocal
)
)
)
endlocal
It is absolutely no problem to run a batch file with one or more spaces in file name on enclosing entire batch file name in double quotes like any other file/folder name containing a space or one of these characters &()[]{}^=;!'+,`~. But this second batch file code demonstrates that it is possible to create the batch files with no spaces in file name while exclamation marks in printer name are nevertheless processed correct.
This code is slower than first code because of usage of setlocal and endlocal inside the loop. Read this answer for details about the commands setlocal and endlocal and what happens on every usage of these two commands in background making the second variant slower than the first variant.
Note: The printer name inside the batch file is with spaces. Just the batch file name is without spaces. But that can be easily changed if needed.
Parenthesis can group commands, but not split arguments. So, instead of ECHO (..., do:
SET printer=%%G
(
ECHO #ECHO OFF
ECHO ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO ECHO.
ECHO ^"%windir%\system32\cscript %windir%\system32\prnmngr.vbs -ac -p \\PRINTSERVER\!printer!"
)>!printer!.bat
Replaced >> with > which will create new files for each printer, instead of
appending more commands to existing files.
Noticed also you don't need delayed expansion, and probably "delims=" is more adequate.
Overall, your code may be rewritten as:
FOR /F "delims=" %%G IN ('wmic /node:PRINTSERVER printer get name') DO (
(
ECHO #ECHO OFF
ECHO ECHO PLEASE WAIT WHILE YOUR PRINTER IS INSTALLED
ECHO ECHO.
ECHO "%%windir%%\system32\cscript" "%%windir%%\system32\prnmngr.vbs" -ac -p \\PRINTSERVER\%%G
)>%%G.bat
)

Batch file redirection to text file not working inside for loop

REM timestamped name of file
set PREFIX=LINE_
set SAVESTAMP=%PREFIX%%DATE:/=-%_%TIME::=-%
set SAVESTAMP=%SAVESTAMP: =%
set SAVESTAMP=%SAVESTAMP:,=.%.txt
echo %SAVESTAMP%
REM In H.txt, for each line grab the text after delimiter ':' and send it to file
for /f "tokens=2 delims=:" %%a in ('type D.txt^|find ":"') do (
set line=%%a
REM#1
echo %line% >> %SAVESTAMP%
)
REM#2
REM echo %line% >> %SAVESTAMP% ---This output one line
When I use REM#1 and comment the second echo outside the for loop, there is not file created.
But when I use REM#2 and don't use REM#1 echo a file with one line is created.
Most times the file will have multiples lines written to it.
So I want this working inside the for loop.
D.txt
line1:sample1
line2:sample2
line3:sample3
line4:sample4
Output should be :
Line_Date_TimeStamp.txt
sample1
sample2
sample3
sample4
Two problems:
First, there must be a separator (like a space) between rem and the text, otherwise an attempt to find and execute an executable named rem#1.(bat,exe etc.) is made.
Second, you need to search SO for delayed expansion as you are attempting to show the value of a variable (line) that is being modified within the for block.

batch file, creating numbering list

I have created a batch file that stores log info, so let say you enter username and pass, it is encrypted and stored on a text file, that works great, the text is stored in this format:
xewiofjeoijfe
casowc43pcj89
And so on, i just used random text by the way on the above lines to use as example, so every new line of text is added to the bottom of the text file, what I'd like to do is that every time a new line is added, is added with a number or name and number, like so:
username1 fewfuhvruivhriuvew
username2 erwve9p8rvejp9
username3 vj39gew4tg000904t3[0g9i40
How can i accomplish this?
If I understand what it is you want to do this should be close:
#echo off
set "LogFile=Test.txt"
if not exist "%LogFile%" type nul>"%LogFile%" & rem this line needed to create empty file if file does not exist
set "YourLoggedInfo=SomeInfoYouLogged" & rem this line will be replaced by what you are currently logging
rem below we will get number of existing lines and add 1
set /a LastLine=0
for /f "usebackq tokens=1 delims=:" %%a in (`findstr /n . "%LogFile%"`) do set /a LastLineNum=%%a
set /a LastLineNum+=1
rem replace %YourLoggedInfo% in the line below with what you are currently logging
echo(Username%LastLineNum% %YourLoggedInfo% >>"%LogFile%"

Batch script Read multi line Argument in Variable

I wanted to take list of files to delete from user as a argument. One line per argument.
How can store the list of files separated by new line in a variable.
I am using below command.
Set DeletionFiles=${p:DeleteFiles}"
for %%i in (%DeletionFiles%) do (
echo %%i
)
Then i wanted to iterated them on a loop.
${p:DeleteFiles} will get replaced by it's value from external app, which will contain list of files separated by new line.I can not change it.
#ECHO OFF
SETLOCAL
SET "deletionfiles="
:dloop
SET "deleteme="
SET /p "deleteme=Which file to delete ? "
IF DEFINED deleteme SET "deleteme=%deleteme:"=%"
IF DEFINED deleteme SET "deletionfiles=%deletionfiles%,"%deleteme%""&goto dloop
ECHO delete %deletionfiles:~1%
GOTO :EOF
There is no need to use a newline. Your for command (or a del command) will operate perfectly happily on a comma-(or space-)separated list.
Note that there are certain characters that batch uses for special purposes and batch string-processing may not process them in the expected manner. These characters include % ^ and &.
${p:DeleteFiles} will get replaced by it's value from external app,
which will contain list of files separated by new line.I can not
change it.
After the replacement the batch file looks like:
Set DeletionFiles=file1.jpg
file2.jpg
file3.jpg
"
This isn't a valid batch file anymore.
Furthermore it's a bad idea to modify the batch file itself, as this works only once.
You could place the ${p:DeleteFiles} into another file, like input.txt.
Your batch would look like
echo ${p:DeleteFiles} > input.txt
<external program for replacing the DeleteFiles> input.txt
for /F "tokens=*" %%A in (input.txt) do (
echo File: %%A
)
If I understand you correctly, your external program will generate a list of files. You then want to store this multi-line list to a variable. What do you want to do with the variable once you have it? I assume you want to delete the files, but your question isn't clear on that point, so I'll try to over-answer to cover it.
for /f "delims=" %%a in ('{command that generates your list}') do (
echo Doing stuff to %%a...
echo %%a>>listOfFilesToDelete.txt
set var=%%a
if "%var:~0,7%"="DoNotDelete" copy "%%a" \someArchiveFolder\
del "%%a"
)
This will read each line in your generated list as variable %%a. It will then do whatever command(s) you specify. This way, you can run a command on each of the files in the list. In the above code it's
Printing each line to the console embedded in some text
Outputting it to a file
Checking the first 7 characters of the line against a specified string and then copying it to a folder if it matches
And then deleting it
If you still need to reference each line from your generated list, you can even setup an array-like structure. (See Create list or arrays in Windows Batch)
setlocal EnableDelayedExpansion
:: Capture lines in an 'array'
set /a i=0
for /f "delims=" %%a in ('dir /b') do (
set /a i+=1
set var!i!=%%a
)
:: Loop through the 'array'
for /L %%a in (1,1,%i%) do (
echo Do more stuff with !var%%a!
)
Just like above, this will read each line in your generated list as variable %%a. It will then set a variable var!i! equal to the value of the current line. You can then reference each line as var1, var2, and so on, or, as the second section shows, you can loop through them all using for /L. You'll need to get a grasp on working with delayed expansion variables, though.

How to get just the first line of a text file written into a new text file using a batch file?

Okay I have several lines in a text file. I want to get the first line and save it in another file. For example this is the text file:
put returns between paragraphs
for linebreak add 2 spaces at end
for linebreak add 2 spaces at end2
for linebreak add 2 spaces at end3
I want put returns between paragraphs to be saved into another file.
I used
for /f "tokens=" %%A in ('findstr /r "^[0-9][0-9]*$" <"C:\Users\Sherlock\Desktop\AbcImport\123.txt"') do echo 123>>1234.txt
pause
But it doesn't work at all.
How to get just the first line of a text file written into a new text file using a batch file?
Option 1 - SET /P : This is the simplest and fastest pure batch solution, provided the line does not exceed 1021 bytes, and it does not end with control characters that must be preserved. The size of the file does not matter - it will always read and write the first line very quickly.
#echo off
setlocal enableDelayedExpansion
set "ln="
<"input.txt" set /p "ln="
>"output.txt" (echo(!ln!)
Option 2 - FOR /F : This will work with lines up to ~8191 bytes long, but it can be slow if the file is really large because the FOR /F loop must read the entire file before it processes the first line. This solution is basically the same as the Mofi answer, except it disables the EOL option, so it never ignores the first line, regardless what the first character is. It does have a limitation that it will skip empty lines, so technically it does not give the correct result if the first line is empty:
#echo off
for /f usebackq^ delims^=^ eol^= %%A in ("input.txt") do echo(%%A>"output.txt"&goto :break
:break
There is a way to preserve the first line if it is empty using pure batch, but I would not bother. I would move on to ...
Option 3 - JREPL.BAT, or some other non-batch solution : Batch is quite poor at manipulating text files. You are much better off using some other scripting language like VBScript, JScript, or Powershell. Or a Windows port of any number of unix utilities.
I would use JREPL.BAT - a hybrid JScrpit/batch regular expression text processing utility that runs natively on any Windows machine from XP onward. It is way overkill for such a simple task, but it is an extremely handy, powerful, and efficient tool to have in your arsenal. Once you have it, then it can be used for many text processing tasks. Full documentation is embedded within the script.
jrepl "^.*" "$&" /jendln "quit=true" /f "input.txt" /o "output.txt"
Use CALL JREPL if you put the command within a batch script.
Here is the batch code to write just first non blank/empty line of a text file into another text file.
#echo off
for /F "usebackq delims=" %%I in ("InputTextFile.txt") do (
echo %%I>"OutputTextFile.txt"
goto ContinueAfterLoop
)
:ContinueAfterLoop
InputTextFile.txt is the file in current directory containing the first line to copy.
OutputTextFile.txt is the file created in current directory with first line from input file copied into this output file.
The command GOTO is used to exit the loop after first line is processed and continue the batch file below the loop.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?
goto /?
Read also the Microsoft article about Using Command Redirection Operators.
You can use use this command:
SetLocal EnableDelayedExpansion
for /f "tokens=* delims=;" %%m in ("C:\Users\Sherlock\Desktop\AbcImport\123.txt") do (
set /p FirstLine=<%%m
echo !FirstLine!>>1234.txt
)
and for multiple file:
SetLocal EnableDelayedExpansion
for %%a in ("*") do (
for /f "tokens=* delims=;" %%m in ("%%a") do (
set /p FirstLine=<%%m
echo !FirstLine!>>1234.txt
)
)
rem Get the first line of a text file:
set /P "line=" < "C:\Users\Sherlock\Desktop\AbcImport\123.txt"
rem Write it into a new text file:
echo %line%> 1234.txt

Resources