I am trying to set a variable using if else statements. The problem is that the else if "%%D"GEQ "%%G" (set var=%%G)
is not executing consistently. I am not sure what is wrong.
Here is my code:
@echo on
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=1-8* delims=," %%A IN (results.csv) DO (
if "%%D" equ 0 (
set var=0
) else if "%%D" GEQ "%%G" (
set var=%%G
) else (set var=%%D)
set "y=%%A,%%B,%%C,%%D,%%E,%%F,%%G,%%H,!var!"
echo !y!>>final.csv
)
Here is a sample of my input file.
"01185901095","11","0379-0005","50","001","0","3","3"
"01185901215","11","0379-0013","138","001","0","4","2"
Here is the output I get in final.csv
"01185901095","11","0379-0005","50","001","0","3","3","3"
"01185901215","11","0379-0013","138","001","0","4","2","138"
My expected output is:
"01185901095","11","0379-0005","50","001","0","3","3","3"
"01185901215","11","0379-0013","138","001","0","4","2","4"
Line 2 in the output is the problem . %%D is greater than %%G so I expect the value of %%G (or 138 is greater than 4 so I expect 4)
CodePudding user response:
I'm not quite sure of the purpose of some of your comparisons, so based purely off best guesses, your input file content and your expected output content, would something like this not do what you wanted:
@( For /F "UseBackQ EOL=, Delims=" %%G In ("results.csv"
) Do @For /F "Tokens=4,7 Delims=," %%H In ("%%~G"
) Do @If %%~H Gtr %%~I (Echo %%G,"%%~I") Else Echo %%G,"%%~H"
) 1>"final.csv"
As you can see, there's no need for delayed expansion, or defining variables.
CodePudding user response:
There should be read first my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files to get full knowledge on how the internal command IF of cmd.exe
makes a string comparison which is done here and not an integer comparison as expected.
The condition if "%%D" equ 0
becomes if ""50"" EQU 0
on processing the first line of the input CSV file results.csv
which results in the comparison of the string ""50""
with the string 0
because of the four double quotes around the value 50
. The first character "
of first string ""50""
has the decimal byte value 34
while the first character 0
of the second string 0
as the decimal byte value 48. The used function lstrcmpW exits for that reason already on comparing the first character of the two strings with −1
. This integer value is compared next on being equal with the integer value 0
independent the fact that there is specified right to the comparison operator EQU
by chance here also 0
. The result of this condition is always false
independent on which string is assigned to the loop variable D
read from the file results.csv
because of the double quotes around %%D
resulting always in comparing the character "
with the character 0
.
There is next executed always if "%%D" GEQ "%%G"
which on processing the first line of results.csv
results in the comparison of the string ""50""
with the string ""3""
. The first two characters of both compared strings are equal. The third character 5
of the first string has with decimal byte value 53
a greater value than third character 3
of the second string with decimal byte value 51
and for that reason the result of the string comparison is the integer value 1
which is greater the integer value 0
and for that reason the second condition is by chance correct true for the first line of results.csv
.
But on processing the second line of results.csv
is compared the string ""138""
with the string ""4""
on which the third character of first string has a lower byte value than the third character of the second string. The result of the string comparison is in this case −1
which is less than the integer value 0
. The comparison result is false
for the second condition on processing the values in second line of the CSV file although the integer value 138
would be greater than the integer value 4
.
The solution is not using "
around the loop variable references at all and additionally remove the double quotes around the values read from the CSV file to really run integer value comparisons and not string comparisons which means using %%~D
and %%~G
.
@echo off
setlocal EnableExtensions EnableDelayedExpansion
(for /F "tokens=1-8* delims=," %%A in (results.csv) do (
if %%~D EQU 0 (
set var=0
) else if %%~D GEQ %%~G (
set var=%%G
) else set var=%%D
echo %%A,%%B,%%C,%%D,%%E,%%F,%%G,%%H,!var!
))>final.csv
endlocal
There is one more important performance modification in the above code among some other not so important small improvements: the entire for
loop is enclosed in round brackets and everything output to standard output stream by the command echo
inside the loop is written into the file final.csv
.
This code results on execution in opening first the file final.csv
for write operations and keeping it open all the time as long as for
is processing next the lines read from results.csv
with finally flushing the data of final.csv
and closing this file after for
finished and closed the file results.csv
.
The code in question results for each line read from results.csv
to open the file final.csv
, seek to end of the file, append the line output with command echo
and then close the file final.csv
. This makes processing thousands or millions of lines in results.csv
much slower than the code above although the file caching mechanisms of Windows avoid really writing the opened, changed and closed file final.csv
on each line to the hard disk.
Note: The code as posted here works only if there are no empty field values in results.csv
which means there is no line in results.csv
with ,,
. The current directory on starting the execution of the batch file must be the directory containing results.csv
as otherwise final.csv
will be an empty file.