7

In batch I always use == when using the if command. (For example: if "19"=="3" echo My computer doesnt know maths)

What about for all of the others (LSS, LEQ, NEQ, etc.)? Isn't there something like != for NEQ, or am I thinking Unix?

The reason I want to use symbols is because I thought someone had said for either text or numbers symbols were more efficient than using the text variants.

Either way, I'd still like to know. Thanks.

Mofi
  • 38,783
  • 14
  • 62
  • 115
  • 2
    You can use `if not str1==str2 ...` but I don't think that there is any symbolic comparison operator other than `==`. If you think about it the symbols that would be most likely are already used for redirection, so there would probably need be some horrible quoting necessary or some other bizarre syntax to be able to use them. I don't know why they never added support for `!=`. – Michael Burr Nov 20 '17 at 05:33
  • 3
    @MichaelBurr: probably because when using delayed expansion, the same horrible [escaping | quoting | bizarre syntax] would be necessary. – Stephan Nov 20 '17 at 06:35

2 Answers2

14

The reason operators like > are not used is because they have special meanings in shell scripts. The > is used to redirect output; < used to redirect input, etc.

The documentation from Microsoft, lists the following operators:

Operator | Description
EQU      | equal to
NEQ      | not equal to
LSS      | less than
LEQ      | less than or equal to
GTR      | greater than
GEQ      | greater than or equal to

In addition the word not is used to negate a condition.

The reason I want to use symbols is because I thought someone had said for either text or numbers symbols were more efficent than using the text variants.

They were probably referring to bash and its large catalog of operators. It provides different operators for integer and string operands.

Burhan Khalid
  • 152,028
  • 17
  • 215
  • 255
12

The Windows command processor internal command IF has by default only two operators:

  1. == which runs a string comparison of the two arguments on equality, i.e. use strcmp with condition being true on strcmp returning 0.
  2. not in combination with == to invert the result of the string comparison on equality, i.e. the condition is true if the two compared strings are not equal.

So the command line

if "19"=="3" echo My computer doesn't know maths

runs strcmp with the strings "19" and "3" which means the compared byte streams are hexadecimal 22 31 39 22 00 and 22 33 22 00. The double quotes are not removed before running the string comparison. The quotes are included in the string comparison.

A help for command IF is output on running in a command prompt window the command if /?. This help explains all options and additional operators which can be used on having command extensions enabled as by default.

There is the option /I to compare the two arguments case-insensitive using stricmp instead of strcmp.

Example:

if /I not "%~1" == "/I" echo First argument is neither /i nor /I.

There are the additional comparison operators EQU, NEQ, LSS, LEQ, GTR, GEQ with enabled command extensions.

The angle brackets < and > are used on Windows command line as redirection operators. So they can't be used as comparison operators on an IF condition. Also exclamation mark ! is not available as operator because it means begin/end of an environment variable reference on having delayed environment variable expansion enabled. Run set /? and setlocal /? and endlocal /? for details on usage of delayed environment variable expansion.

Windows command interpreter tries to convert both argument strings to signed 32-bit integers using strtol with base 0 (automatic detection of base) on usage of EQU, NEQ, LSS, LEQ, GTR, GEQ. An integer comparison is done if that is successful for both argument strings because the two compared strings are

  • decimal numbers with first character being optionally - or + and all other characters are decimal digits 0123456789 with first digit not being 0 like -2147483648, -200, +10, 32, 2147483647, or
  • hexadecimal numbers with first character being optionally - or + and next with 0x or 0X and all other characters are hexadecimal digits 0123456789ABCDEFabcdef like -0x80000000, -0XC8, +0x0a, 0x20, 0x7fffFFFF, or
  • octal numbers with first character being optionally - or + and next 0 and all other characters are octal digits 01234567 like -020000000000, -0310, +012, 040, 017777777777.

Otherwise the two argument strings are compared again with strcmp or with stricmp on using additionally /I with operator EQU, NEQ, LSS, LEQ, GTR, GEQ and the comparison operator is applied on the integer result of the string comparison function.

Note: 08 and 09 like other by people interpreted decimal numbers with one or more leading 0 containing 8 or 9 are interpreted as invalid octal numbers and therefore result in a string instead of an integer comparison.

The conversion of both string arguments to signed 32-bit integers needs some extra processor instructions (some nanoseconds or microseconds depending on CPU performance). An integer comparison is therefore a bit slower, but usually not really noticeable slower.

Examples:

if 014 EQU 12 echo Octal number 014 is equal decimal number 12.
if 0x0C EQU 12 echo Hexadecimal number 0C is equal decimal number 12.
if /I 0X0C EQU 014 Hexadecimal number 0C is equal octal number 014.

The option /I is ignored on using a comparison operator other than == and both strings can be converted successfully to 32-bit signed integers. This is proven by the third line above. /I is taken only into account on using the operators EQU, NEQ, LSS, LEQ, GTR, GEQ if one of the two strings cannot be converted successfully to an integer like in the following example:

if /I "0X20" EQU "0x20" echo String "0X20" is case-insensitive equal string "0x20".

If one of the two arguments is enclosed in double quotes on usage of EQU, NEQ, LSS, LEQ, GTR, GEQ, or one of the two strings is not a string representing a valid integer number, the comparison is always done with usage of strcmp or stricmp depending on usage of /I. strcmp and stricmp return both an integer as result which can be a negative number, zero, or a positive number. This integer result is compared with integer value 0 according to the used operator.

Examples:

if 010 NEQ "10" echo String 010 is not equal string "10".
if "100" LSS "20" echo String "100" is less than string "20".

On second example the second character 1 on left side has a lower code value (49 = 0x31) as the second character 2 on right side (50 = 0x32) which results in strcmp returning a negative value resulting in function result LSS 0 being true.

Please note that Windows environment variables are always of type string and need to be converted always from string to integer on using integer comparison or integer arithmetic.

It is advisable in most cases to use string1 == string2 or not string1 == string2 instead of string1 EQU string2 or string1 NEQ string2 on comparing two strings not representing integer values to use directly strcmp or stricmp. Otherwise on comparing strings with EQU or NEQ just some nanoseconds or microseconds are wasted by letting Windows command processor first use strtol which fails to convert one of the two strings to compare and therefore cmd.exe runs next strcmp or stricmp as it would be done immediately on usage of operator ==.

One more important fact:

A string instead of an integer comparison on usage of one of the comparison operators EQU, NEQ, LSS, LEQ, GTR, GEQ is processed by cmd.exe on execution of IF condition only in case of one of the two arguments contains an invalid character. An integer comparison is done nevertheless on an out of range condition like one argument is lower than -2147483648 or greater than 2147483647 as discussed at weird results with IF.

The value range limitation can be worked around by comparing two values as strings on which both value strings have the same number of characters. Here is an example to find out if a file has two or more GiB, i.e. file size is 2147483648 or more bytes.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
if "%~1" == "" ( set "FileName=%~f0" ) else set "FileName=%~1"
for %%I in ("%FileName%") do set "FileSize=000000000000000%%~zI"
if "%FileSize:~-16%" GEQ "0000002147483648" (
    echo "%FileName%" is greater or equal 2 GiB.
) else echo "%FileName%" is less than 2 GiB.
endlocal
pause

The file size of the file of which name is passed to the batch file is assigned to environment variable FileSize as string with always at least 15 additional zero digits at beginning.

And next is compared the FileSize string with just last 16 digits with string 0000002147483648 representing 2 GiB in bytes. strcmp compares the two strings of equal length byte by byte whereby each byte of the two compared strings can have only the hexadecimal values 0x30 to 0x39. strcmp returns immediately with a negative value if the current byte from left string is lower than the current byte from right string which means the file size is lower than 2 GiB. strcmp returns immediately with a positive value if the current byte from left string is greater than the current byte from right string which means the file size is greater than 2 GiB. strcmp returns with zero on the two strings are 100% identical which means the file size is exactly 2 GiB.

Please note that comparing values using a string comparison requires that both values have the same number of characters for an accurate result. The value string with less digits must be prepended with the right amount of 0.

Mofi
  • 38,783
  • 14
  • 62
  • 115