I have an environment variable like this
set BINARY[0]=C:\binary.bin
From which I'm trying to extract the full file name
set "x=0"
:binloop
if defined BINARY[%x%] (
call echo %%BINARY[%x%]%%
FOR %%i IN ("%%BINARY[%x%]%%") DO (
set FNAME=%%~nxi
)
set /a "x+=1"
GOTO binloop
)
rem ...
However for some reason, it tries to do:
set FNAME=%BINARY[0]%
instead of
set FNAME=binary.bin
What's wrong with the code and why?
Open a command prompt window, run set /? and read the output help pages explaining when and how to use delayed expansion in a code block for the commands IF and FOR.
%% in a batch file is interpreted as literal percent character which is the reason why a loop variable in a command executed directly in a command prompt window must be specified with just one percent sign while the same loop in a batch file requires two percent signs on referencing the loop variable.
When the Windows command processor encounters an opening parenthesis which marks the beginning of a command block, it searches for the matching closing parenthesis and replaces all environment variables references with syntax %VariableName% by the current value of the variable or nothing in case of variable does not exist. Then after the entire command block was parsed the IF or FOR is executed and used is once or more times the already preprocessed command block.
You could use
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "BINARY[1]=C:\binary1.bin"
set "BINARY[0]=C:\binary0.bin"
set "x=0"
:binloop
if defined BINARY[%x%] (
call echo %%BINARY[%x%]%%
for %%i in ("!BINARY[%x%]!") do (
set FNAME=%%~nxi
set FNAME
)
set /a "x+=1"
goto binloop
)
endlocal
which outputs
C:\binary0.bin
FNAME=binary0.bin
C:\binary1.bin
FNAME=binary1.bin
The command line
call echo %%BINARY[%x%]%%
is something special. This line is preprocessed before execution of command IF to
call echo %BINARY[0]%
respectively on second run to
call echo %BINARY[1]%
By usage of command CALL the single command line is processed like a subroutine or another batch file which means the line is preprocessed once more resulting in execution of
echo C:\binary0.bin
and on second run in execution of
echo C:\binary1.bin
which is the reason why the output is as expected here. But there is no double preprocessing for the environment variable reference in FOR.
Much better would be most likely the following code:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "BINARY[1]=C:\binary1.bin"
set "BINARY[0]=C:\binary0.bin"
for /F "tokens=1* delims==" %%I in ('set "BINARY[" 2^>nul') do (
set "FNAME=%%~nxJ"
set FNAME
)
endlocal
The command set outputs all variables with their name and equal sign and their values which start with the specified string when there is whether parameter /A or /P used and the parameter does not contain an equal sign in an alphabetically sorted list. So the output of
set "BINARY[" 2>nul
as used in the command FOR is
BINARY[0]=C:\binary0.bin
BINARY[1]=C:\binary1.bin
which is processed by the FOR loop which splits each line into two strings based on first occurrence of the equal sign because of tokens=1* delims==. The first string is the variable name assigned to loop variable I. And the second string is everything after first equal sign assigned to loop variable J being the next character in ASCII table.
2>nul is used to suppress the error message output by command SET to STDERR by redirecting it to device NUL if there is no environment variable defined with a name starting with BINARY[ in any case. The redirection operator > must be escaped with ^ as otherwise command processor would exit batch processing on this line because of 2>nul resulting in a syntax error on FOR command line at this position.
Note: Because of alphabetically sorted output by command SET the environment variable BINARY[10] is output after BINARY[0] and before BINARY[1] and BINARY[2]. So if the order is important, the first batch solution is needed or the environment variables are created with number in square brackets have all same number of digits with leading zeros, i.e. 00000, 00001, ..., 00002, 00010, 00011, ...
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.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
And see also Microsoft article about Using command redirection operators.
Related
I have an XML file in the following manner:
<pools>
<pool>aaa</pool>
<pool>bbb</pool>
<pool>ccc</pool>
<pool>ddd</pool>
<pool>eee</pool>
</pools>
I want to parse these tags in such a way that they will be assigned to variables as
Pool1 = aaa
Pool2 = bbb
and so on
I have tried the below code:
echo off
set /a x=0
SETLOCAL enableextensions enabledelayedexpansion
for /f "tokens=2 delims=<>" %%a in ('find /i "<pool>" ^< "pool_info.xml"') do (
set /a "x+=1"
call ECHO pool%%x%%=%%a
)
And it just prints them properly. I tried the set command for assigning them, but it does not work.
I went through many Stack Overflow problems, but was not able to find any solution that would match my requirement. If anyone could please help me out.
PS: The <pool> tags count here is 5, however, the count can change, so I want it to be flexible.
The task can be done with:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Delete all environment variables of which name starts with Pool.
for /F "delims==" %%I in ('set Pool 2^>nul') do set "%%I="
set "PoolCount=0"
for /F "tokens=2 delims=<> " %%I in ('%SystemRoot%\System32\findstr.exe /I /L /C:"<pool>" "pool_info.xml"') do (
set /A PoolCount+=1
call set "Pool%%PoolCount%%=%%I"
)
rem Output all environment variables of which name starts with Pool.
set Pool
endlocal
ATTENTION: The delimiters are the two angle brackets, a horizontal tab character and a normal space character. Please make sure that the batch file contains exactly those four characters after delims= in that order.
The horizontal tab and the normal space are needed as delimiters to have a working solution independent on leading spaces/tabs on the lines with the pool elements.
The wrong token respectively the missing delimiters tab/space resulted with posted code in question in getting element name pool output instead of the values of the XML element pool.
There is no need to use delayed environment variable expansion in this case.
However, the usage of call to force a second parsing of the command line
call set "Pool%%PoolCount%%=%%I"
modified already during parsing of the entire command block to
call set "Pool%PoolCount%=%I"
before execution of set is slower in comparison to using delayed expansion as used in the code below.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Delete all environment variables of which name starts with Pool.
for /F "delims==" %%I in ('set Pool 2^>nul') do set "%%I="
set "PoolCount=0"
for /F "tokens=2 delims=<> " %%I in ('%SystemRoot%\System32\findstr.exe /I /L /C:"<pool>" "pool_info.xml"') do (
set /A PoolCount+=1
set "Pool!PoolCount!=%%I"
)
rem Output all environment variables of which name starts with Pool.
set Pool
endlocal
The reason is explained by jeb in the DosTips forum post CALL me, or better avoid call. The Windows command processor searches with using call set "Pool%%PoolCount%%=%%I" in the batch file in current directory and next in all directories of environment variable PATH for a file matching the wildcard pattern set.*. If there is indeed a file found like set.txt in one of the directories, it searches next in that directory for set.COM, set.EXE, set.BAT, set.CMD, ... according to list of file extensions of environment variable PATHEXT. If there is really an executable or script found by cmd.exe with file name set in current directory or another other directory of PATH with a file extension of PATHEXT, it executes the executable/script instead of running internal command SET.
For that reason it is definitely better to use delayed expansion solution as it is faster and more safe.
The disadvantage is that a pool value with one or more ! is not correct processed with enabled delayed expansion. So once again cmd.exe proves itself that the Windows command processor is designed for executing commands and executables, but not for processing data in text files.
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.
call /? ... used for double parsing the command line before execution of set.
echo /?
endlocal /?
findstr /?
for /?
rem /?
set /?
setlocal /?
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 set command line with using a separate command process started in background with %ComSpec% /c and the command line within ' appended as additional arguments.
...
set /a "x+=1"
call SET pool%%x%%=%%a
)
SET pool
The first set assigns the value in %%a to the variable pool?
The second set displays all of the currently-set environment variables whose name starts pool.
setx is a command designed to record a variable assignment for future instances of cmd. It's an entirely different matter and should be raised as a separate question, but there's plenty of SO items about setx so raising it (again) as a separate issue will likely be closed as a duplicate. Best use the search facility for setx.
In my second if statement, I want to filter out "tool" or "tool.bat" from the final list of filenames. However, the final list of filenames includes "tool" and total_bags is being incremented. I was wondering what I did incorrectly that's causing the program to not catch this case.
set /A total_bags=0
set target=%~1
if "%target%"=="" set target=%cd%
set LF=^
rem Previous two lines deliberately left blank for LF to work.
for /f "tokens=1 delims=. " %%i in ('dir /b /s /a:-d "%target%"') do (
set current_file=%%~ni
echo !unique_files! | find "!current_file!:" > nul
if NOT !ERRORLEVEL! == 0 (
if NOT !current_file! == "tool.bat" (
set /A total_bags=total_bags+1
set unique_files=!unique_files!!current_file!:
)
)
)
echo %unique_files::=!LF!%
echo %total_bags%
endlocal
The condition if NOT "%current_file%" == "tool.bat" as initially used does not work because of %current_file% is replaced already by current string of the environment variable current_file respectively an empty string on Windows command processor is processing the entire command block starting with ( and ending with matching ) before executing command FOR. That can be seen on debugging the batch file. See also Variables are not behaving as expected for a very good and short example explaining how the Windows command interpreter (CMD.EXE) parses scripts.
It is in general not advisable to assign the string already assigned to a loop variable to an environment variable which is not further modified inside a FOR loop. It would be better to use %%~ni everywhere in your code on which the current file name needs to be referenced.
The usage of delayed expansion requires enabling it with setlocal EnableDelayedExpansion (or with setlocal EnableExtensions EnableDelayedExpansion to enable explicitly also the command extensions enabled by default) as it is not enabled by default in comparison to the command extensions. Then the Windows command processor parses each command line a second time and expands !current_file! on execution of command IF.
But even if NOT !current_file! == "tool.bat" evaluates always to true for the batch file with name tool.bat because of set current_file=%%~ni results in assigned to the environment variable current_file only the string tool (file name without file extension) and the left string is not enclosed in double quotes while the right string is always enclosed in double quotes. The command IF does not remove the double quotes from right string before comparing the two strings.
The batch file in question misses also set unique_files= above the FOR loop to undefine explicitly the environment variable unique_files in case of being already defined by chance on starting the batch file, for example from a previous execution within a command prompt window.
Another problem with the batch file in question is that maximum string length of variable name + equal sign + string assigned to the environment variable is 8191 characters which is a problem on several thousands of file names are concatenated to a long string assigned to one environment variable like unique_files.
I suggest to use this batch file with comments explaining it.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Delete all environment variables of which name starts very unusual
rem with a question mark existing already by chance (with exception of
rem those environment variables with multiple question marks in name).
for /F "delims=?" %%I in ('set ? 2^>nul') do set "?%%I?="
rem Search with the string passed as first argument or simply within current
rem directory recursively for all files and define for each file name an
rem environment variable with a question mark at beginning and one more at
rem end of the variable name. A file name cannot contain a question mark.
rem The value assigned to the environment variable does not matter. As it
rem is not possible to define multiple environment variables with same name
rem and environment variable names are case-insensitive, there is just one
rem environment variable defined on multiple files have same file name.
rem The batch file itself is ignored because of the IF condition.
for /F "delims=" %%I in ('dir "%~1" /A-D /B /S 2^>nul') do if not "%%I" == "%~f0" set "?%%~nI?=1"
rem Initialize the file counting environment variable.
set "FileCount=0"
rem Output all file names which are the environment variable names sorted
rem alphabetically with the question marks removed and additionally count
rem the number of file names output by this loop.
for /F "eol=| delims=?" %%I in ('set ? 2^>nul') do set /A "FileCount+=1" & echo %%I
rem Output finally the number of unique file names excluding file extensions.
echo %FileCount%
rem Restore initial execution environment which results also in the
rem deletion of all environment variables defined during batch execution.
endlocal
It does not use delayed expansion and for that reason works also for file names containing one or more ! in file name which would be processed wrong on enabling delayed expansion on line set current_file=%%~ni because of the exclamation mark(s) in file name would be interpreted as begin/end of a delayed expanded environment variable reference.
There is defined an environment variable for each unique file name. The number of environment variables is limited only by the total available memory for environment variables which is 64 MiB. That should be enough even for several thousands of unique file names in the directory tree.
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.
call /? ... explains %~f0 which references full name of argument 0 which is the full qualified file name of the currently processed batch file and %~1 referencing first argument with perhaps existing surrounding " removed from argument string.
dir /?
echo /?
endlocal /?
for /?
if /?
rem /?
set /?
setlocal /?
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 the FOR command lines to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir or set command line with using a separate command process started in background with %ComSpec% /c and the command line within ' appended as additional arguments.
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?
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.
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.
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:
Disable command echo mode.
Push current command extension state on stack and enable command extensions.
Push current delayed expansion state on stack and disable delayed environment variable expansion.
Push path of current directory on stack.
Push pointer to current list of environment variables on stack and create a copy of the entire current environment variables list to use next.
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:
Discard the current list of environment variables and pop from stack the pointer to initial list of environment variables resulting in restoring the initial list of environment variables. In other words all environment variables defined or modified by the batch file after command SETLOCAL in second command line are lost forever.
Pop path of current directory from stack and make this directory again the current directory. The current directory between setlocal and endlocal was not changed by the code between and so this does not matter here.
Pop delayed expansion state from stack and enable or disable delayed environment variable expansion accordingly to restore initial delayed expansion behavior.
Pop current command extension state from stack and enable or disable command extensions accordingly to restore initial command extension behavior.
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.
call /?
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
See also:
Variables are not behaving as expected
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Microsoft's documentation for the Windows Commands
SS64.com - A-Z index of Windows CMD commands
I want to insert data (only rows having extended keyword) present in .txt file into Oracle database in the format ID,Data,Date,Project Name where ID, date and project name are present in environment variables.
File.txt has below data:
Writing main object(name=abc)
writing Extended object (name=%abc(123&rest,type=pqr)
logdata.txt should have below data:
A1234C,(name=%abc(123&rest,type=pqr),12022018_11:12:20,DEV:Sales Project
While copying the data, special characters like %,( etc present in the file.txt are missing in the output file logdata.txt.
Please find below code :
set file=D:\MSTR_CICD\file.txt
for /F "usebackq tokens=2*delims=(" %%a in (`findstr "extended" "%file%"`) do (
set /A i+=1
call set array[%%i%%]=%%a
call set n=%%i%%
)
for /L %%i in (1,1,%n%) do call echo %User_ID%,%%array[%%i]%%,%Timestamp%,%proj% >> D:\MSTR_CICD\Batch_Script\logdata.txt
Please correct the code or let me know how can i achieve this. Also, my input file can have any special character as it contain logs of an application.
This batch file can be used for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "proj=DEV:Sales Project"
set "User_ID=A1234C"
set "Timestamp=12022018_11:12:20"
set "InputFile=D:\MSTR_CICD\file.txt"
set "DataFile=D:\MSTR_CICD\Batch_Script\logdata.txt"
if exist "%InputFile%" (
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /I /C:Extended "%InputFile%"') do (
set "DataLine=%%I"
setlocal EnableDelayedExpansion
set "DataLine=!DataLine:*(=(!"
set "DataLine=!DataLine:"=""!"
echo %User_ID%,"!DataLine!",%Timestamp%,%proj%
endlocal
)
) >"%DataFile%"
if exist "%DataFile%" for %%I in ("%DataFile%") do if %%~zI == 0 del "%DataFile%"
:EndBatch
endlocal
FINDSTR runs in the separate command process started by FOR in background with cmd.exe /C a case-insensitive, literal search for the string Extended on the input file and outputs all lines containing this string to handle STDOUT.
FOR captures this output and processes them line by line. FOR ignores empty lines and by default also lines starting with a semicolon because of ; is the default end of line character. And FOR splits up the line into substrings (tokens) using space/tab as delimiter and assigns just the first substring to specified loop variable by default.
By using the FOR option string delims^=^ eol^= an empty list of delimiters and and no end of line character is set to disable line splitting and ignoring lines starting with a semicolon. As this special option string cannot be enclosed in double quotes, it is necessary to escape the space and the two equal signs with caret character to get those three characters outside a double quoted argument string interpreted as literal characters and not as argument string separators.
The entire line as output by FINDSTR found in file is assigned to environment variable DataLine. This is done with delayed environment variable expansion disabled to process also lines correct containing one or more exclamation marks. Otherwise cmd.exe would double parse the line set "DataLine=%%I" after having replaced %%I by the current line and would interpret every ! in the line as begin/end of an environment variable reference resulting in unwanted modification of the line before assigning it to the environment variable.
The usage of command CALL on a line with command SET results also in double parsing the command line before executing the command SET which is the reason why some characters are missing in the environment variables array produced by your code.
For details see also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
After having assigned the line to the environment variable, it is necessary to enable delayed expansion to further process the data line in the FOR loop. That makes the batch file slow, but can't be avoided in this case. Read this answer for details about the commands SETLOCAL and ENDLOCAL.
The first modification on the data line is removing everything left to first (.
The second modification on the data line is replacing all " by "" in the line to escape every double quote according to CSV specification.
Then the remaining data line is output together with the other data enclosed in double quotes as the data line can contain also one or more commas which requires according to CSV specification that the data is enclosed in double quotes.
For CSV specification read for example the Wikipedia article about comma-separated values.
Everything output by ECHO inside FOR loop is redirected to the specified data file which overwrites a by chance already existing data file with same name.
It is possible that FINDSTR does not find any line containing Extended in any case resulting in producing a data file with 0 bytes. The empty data file is deleted by the second FOR.
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.
del /?
echo /?
endlocal /?
findstr /?
for /?
goto /?
if /?
set /?
setlocal /?
I am trying to compare the logs of a file with a plain string, but it is not getting into if comparison. I am getting " Connect failed" as my 2nd token in echo statement, but not getting any result of IF statement.
#echo off
rem start cmd.exe
for /f "tokens=2 delims=:" %%n IN (C:\Users\rohit.bagjani\Desktop\result\telnetresult.txt) DO (
SET str1 = " Connect failed"
echo %%n
if \i %str1%==%%n echo "true"
)
echo.
pause
The first mistake is in line:
SET str1 = " Connect failed"
This line defines an environment variable with name str1 with a space at end of name with the value " Connect failed" assigned to it. The leading space and the two double quotes are also assigned to the variable as part of the string.
As the answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? explains in detail, the right syntax would be:
set "str1=Connect failed"
This command line defines an environment variable str1 with the value Connect failed assigned to it.
Run in a command prompt window set /? to get displayed the help for this command on several display pages.
The second mistake is in line:
if \i %str1%==%%n echo "true"
Options/switches are on Windows specified with / and \ is used as directory separator. So the switch for case-insensitive comparison must be /i and not \i.
Run in a command prompt window if /? for help on IF command.
The third mistake is the attempt to define an environment variable within a command block with assigning a string value to the environment variable and reference the value of this environment variable not using delayed expansion in same command block.
Whenever Windows command interpreter encounters an opening round bracket ( being interpreted as begin of a command block, it parses everything to matching parenthesis ) and replaces all environment variable references done with %VariableName% by current value of the environment variable.
In posted code this means the line
if \i %str1%==%%n echo "true"
is changed by Windows command interpreter to
if \i == %n echo "true"
before FOR is executed at all because of environment variable str1 is not defined above the FOR command block.
This can be easily seen by changing echo off to echo on or remove the line with echo off or comment it out with command rem and run the batch file from within a command prompt window. Then Windows command interpreter outputs each command block and each command line after preprocessing before execution.
Double clicking on a batch file to execute it is not good as the window is automatically closed on an exit of batch processing because of a syntax error like this one. The usage of pause is no help as this command line is not reached at all on detecting a syntax error by cmd.exe.
A solution would be:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq tokens=2 delims=:" %%I in ("%USERPROFILE%\Desktop\result\telnetresult.txt") do (
set "str1=Connect failed"
echo %%I
if /I "!str1!" == "%%~I" echo true
)
endlocal
echo/
pause
But much easier and also working would be:
#echo off
for /F "usebackq tokens=2 delims=:" %%I in ("%USERPROFILE%\Desktop\result\telnetresult.txt") do (
echo %%I
if /I "Connect failed" == "%%~I" echo true
)
echo/
pause
For the reason using echo/ instead of echo. to output an empty line see What does an echo followed immediately by a slash do in a Windows CMD file?
The usage of I or any other upper case letter instead of n as loop variable is more safe. Why? Run in a command command window for /? and read the output help explaining also %~nI. On usage of %%~n in a batch file it could be unclear for Windows command interpreter if the current value of loop variable n should be used with surrounding double quotes removed or there is a syntax error as the loop variable is missing after modifier ~n. Loop variables are case-sensitive. The usage of upper case letters avoids conflicts in interpretation of loop variables with modifiers.
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 /?
endlocal /?
for /?
if /?
pause /?
set /?
setlocal /?
See Wikipedia article about Windows Environment Variables for a list of predefined environment variables with description like USERPROFILE.