Home > Net >  Batch if else not executing consistently
Batch if else not executing consistently

Time:10-23

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.

  • Related