Reputation: 5
I am trying to figure out how to read IP addresses from a file named "IPList.txt) into individual variables in a batch script. Here's what I have so far.
:DEFINITIONS
set LOGFILE=IPScript.log
set IPLIST=C:\IPLIST.txt
echo Script Started >> %LOGFILE%
goto SetIP
:SetIP
for /f "tokens=*" %%a in (%IPLIST%) do (
set FirstIP=%%a
)
echo The first IP is %FirstIP% >> %LOGFILE%
exit
The output I'm getting in "IPscript.log" is "The First IP is: " with no IP listed, just a space. Also, is there a way for me to set multiple IPs like this, in just one for loop?
Upvotes: 0
Views: 1841
Reputation: 49086
The batch file could have following command lines:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=%~dp0IPScript.log"
set "IPLIST=%~dp0IPLIST.txt"
set "AddressCount=0"
echo Script started>"%LOGFILE%"
for /F "delims==" %%I in ('set IP_Address_ 2^>nul') do set "%%I="
if exist "%IPLIST%" for /F "useback delims=" %%I in ("%IPLIST%") do (
set /A AddressCount+=1
call set "IP_Address_%%AddressCount%%=%%I"
)
if not %AddressCount% == 0 (
if %AddressCount% == 1 (
echo The IP address is:
) else echo The IP addresses are:
echo(
set IP_Address_
) >>"%LOGFILE%"
endlocal
The batch file first two command line define the execution environment which means:
The third and fourth line define two environment variables with the name of the log file and the name of the IP address list file with full qualified file name. The file path of both files is defined as path of the directory containing the batch file referenced with %~dp0
. This path always ends with \
and for that reason no additional backslash is needed on concatenating this path with the two file names.
The fifth line define the environment variable AddressCount
with value 0
.
The sixth line creates the log file in current directory with overwriting an already existing log file. There is no space left to redirection operator >
as this space would be output by command ECHO and therefore written as trailing space also into the log file.
The first FOR command with option /F
starts in background with %ComSpec% /c
one more command process with the command line between '
appended as additional arguments. So executed is in background with Windows installed into C:\Windows
:
C:\Windows\System32\cmd.exe /c set IP_Address_ 2>nul
Windows creates a copy of current list of environment variables for the command process started in background. The background command process runs command SET to output all environment variables with name, an equal sign and the string value assigned to the variable line by line of which name starts with IP_Address_
. This output to handle STDOUT of background command process is captured by FOR respectively the command process which is processing the batch file. The error message output by SET on no environment variable define with a name starting with IP_Address_
is redirected from handle STDERR to device NUL to suppress this error message.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul
. The redirection operator >
must be escaped with caret character ^
on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir
command line with using a separate command process started in background.
FOR processes the captured output line by line after started background command process closed itself after execution of command SET. Empty lines are always ignored by FOR which can be ignored as there are no empty lines output by SET.
FOR would split up by default the current line into substrings using normal space and horizontal tab as delimiters. This default line splitting behavior is not wanted here. The option delims==
defines the equal sign as string delimiter to split the line on =
which is the character between variable name and variable value.
FOR would next ignore the line if the first substring would start with a semicolon which is the default end of line character. The command SET outputs only lines starting with IP_Address_
and for that reason the default eol=;
can be kept in this case.
FOR assigns just the first substring to the specified loop variable I
as tokens=1
is the default. That is exactly the wanted behavior in this case.
So FOR assigns one environment variable name starting with IP_Address_
to loop variable I
and runs next the command SET to delete this environment variable in current list of environment variables of command process processing the batch file.
In other words the first FOR is for deletion of all environment variables of which name starts with IP_Address_
defined by chance outside the batch file.
The next line first checks if the file with the list of environment variables exists at all in directory of the batch file. In this case once again FOR is used to process lines, but this time read line by line from the specified list file instead of captured output of a background command process. The usage of "
instead of '
with the option usebackq
makes the difference.
There is used the option delims=
to define an empty list of delimiters resulting in getting each non-empty line not starting with ;
assigned completely to the specified loop variable I
.
For each string assigned to loop variable I
the current value of environment variable AddressCount
is incremented by one using an arithmetic expression evaluated by command SET.
This value is used on next command line to define an environment variable of which name starts with IP_Address_
and has appended the current address count value with line read from file assigned to the environment variable.
There is usually used delayed expansion for such tasks on which the second command line in command block of second FOR loop would be:
set "IP_Address_!AddressCount!=%%I"
But the code above uses the alternative method with command call
to parse set "IP_Address_%%AddressCount%%=%%I"
a second time which was already modified to set "IP_Address_%AddressCount%=%I"
before the IF condition left to FOR was executed at all.
The next IF condition checks if any line was read from the list file with the IP addresses. In this case first an information line is output depending on having read exactly one line from the file or more than one line. Then an empty line is output and last all environment variables of which name starts with IP_Address_
with =
and the line (IP address) assigned to the environment variable. All this output is appended to the log file.
The last command restores previous execution environment which means:
setlocal
and endlocal
was not changed by the code between and so this does not matter here.To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
call /?
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
See also:
Upvotes: 1
Reputation: 38604
Here's a quick example to assist you:
@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
:DEFINE_LOCAL_VARIABLES
Set "IPLIST=C:\IPLIST.txt"
Set "LOGFILE=IPScript.log"
:CHECK_SOURCE_EXISTS
For %%G In ("%IPLIST%") Do If "%%~aG" Lss "-" (
Echo The file %IPLIST% does not exist.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
) Else If "%%~aG" GEq "d" (
Echo Expected a file, but %IPLIST% is a directory.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:UNDEFINE_LOCAL_VARIABLES
For /F "Delims==" %%G In ('"(Set IP[) 2> NUL"') Do Set "%%G="
:START_MAIN
Set "i=1000"
(
Echo Script Started
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%IPLIST%") Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
For %%H In ("!i:~-3!") Do (
EndLocal
Set "IP[%%~H]=%%G"
Echo IP[%%~H] is %%G
)
)
) 1> "%LOGFILE%"
:CHECK_IP_VARIABLES_EXIST
If Not Defined IP[001] (
Echo %IPLIST% had no readable file content.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:VIEW_IP_VARIABLES
Set IP[
Pause & GoTo :EOF
If you have an existing %LOGFILE%
, and you intend to append to it, (as opposed to overwrite/create one), change 1> "%LOGFILE%"
to 1>> "%LOGFILE%"
.
If you didn't really need %LOGFILE%
, e.g. it was used by you just for testing, it would look a little more like this:
@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
:DEFINE_LOCAL_VARIABLES
Set "IPLIST=C:\IPLIST.txt"
:CHECK_SOURCE_EXISTS
For %%G In ("%IPLIST%") Do If "%%~aG" Lss "-" (
Echo The file %IPLIST% does not exist.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
) Else If "%%~aG" GEq "d" (
Echo Expected a file, but %IPLIST% is a directory.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:UNDEFINE_LOCAL_VARIABLES
For /F "Delims==" %%G In ('"(Set IP[) 2> NUL"') Do Set "%%G="
:START_MAIN
Set "i=1000"
Echo Script Started
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%IPLIST%") Do (
Set /A i += 1
SetLocal EnableDelayedExpansion
For %%H In ("!i:~-3!") Do (
EndLocal
Set "IP[%%~H]=%%G"
)
)
:CHECK_IP_VARIABLES_EXIST
If Not Defined IP[001] (
Echo %IPLIST% had no readable file content.
Echo Press any key to end this script.
Pause 1> NUL
GoTo :EOF
)
:VIEW_IP_VARIABLES
Set IP[
Pause & GoTo :EOF
The last line in both examples is for display purposes. If you're testing/running this script from within cmd.exe
, you may omit it.
Upvotes: 1
Reputation: 80013
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r ".*" "%filename1%"') DO set "IP%%a=%%b"
)
set IP
findstr
reads the file in filename1
and produces a list of the format n:content of line n
.
The for /f
reads this list, and partitions it using 2 tokens - %%a
gets the first token (1) and %%b
the remainder of the line (*) using :
as a delimiter.
So simply set the IP
variables from there.
set ip
displays all variables that start ip
Probability is that your file contains empty line(s) after the last IP. Your original code would have reported the LAST IP, not the FIRST as the value in firstip
is overwritten on each iteration, so it would be cleared by being set to nothing when the empty lines are read.
The solution above would simply execute (eg) set "IP6="
under these circumstances, clearing the variable.
You could have obtained the first IP by using
if not defined firstip set "FirstIP=%%a"
I'm assuming a clean environment here - that is, that each batch you run includes a setlocal
after the @echo off
(which restores the initial environment when the batch finishes) and the variables used are known-empty.
Bonus:
changing the set
command to
set "IP%%a=%%b"&if "%%b" neq "" set "ipmax=%%a"
would set ipmax
to the number of the last non-empty line, as %%b
is empty for an empty line.
Upvotes: 1