Alan Ngo
Alan Ngo

Reputation: 13

What to fix on my code with two IF conditions inside a FOR loop referencing an environment variable defined by the IF conditions?

Could you please guide me how to fix my script to get the trace route results to some IP addresses?

I want to get the tracert results for 8.8.8.8 and 8.8.4.4. I wrote the script below and it works fine:

@echo off
echo %date% %time%

SET DNS1=8.8.8.8
SET DNS2=8.8.4.4

for  %%i in (%DNS1% %DNS2%) do (
    echo ----------------
    echo tracert to %%i
    tracert -d %%i
)

echo %time%
exit

I want to add a function so that it can print host name automatically. I tried as below, but it doesn't work.

@echo off
echo %date% %time%

SET DNS1=8.8.8.8
SET DNS2=8.8.4.4

for  %%i in (%DNS1% %DNS2%) do (
    echo ----------------
    if (%%i == 8.8.8.8 set host=Google1
        %%i == 8.8.4.4 set host=Google2)
    echo tracert to %host%
    tracert -d %%i
)

echo %time%
exit

Could somebody please correct the code for me?

Upvotes: 1

Views: 62

Answers (2)

Mofi
Mofi

Reputation: 49097

The batch file could be written like this:

@echo off
echo %DATE% %TIME%

setlocal EnableExtensions EnableDelayedExpansion
set "DNS1=8.8.8.8"
set "DNS2=8.8.4.4"

for %%i in (%DNS1% %DNS2%) do (
    echo ----------------
    if %%i == 8.8.8.8 set "host=Google1"
    if %%i == 8.8.4.4 set "host=Google2"
    echo tracert to !host!
    tracert -d %%i
)

endlocal
echo %TIME%
exit /B

Windows command processor executes one command line after the other. How a command line is parsed before execution is described at How does the Windows Command Interpreter (CMD.EXE) parse scripts? What is executed finally after parsing a command line can be seen on executing a batch file without @echo off from within a command prompt window as described at debugging a batch file.

A command block starting with ( and ending with matching ) is parsed completely by cmd.exe before the command is executed with makes conditionally or unconditionally use of the command block. During parsing the command block all environment variable references using syntax %variable% like %host% are substituted by current value of the referenced environment variable. In this case %host% is most likely replaced by an empty string before FOR is executed at all if the environment variable host is not defined by chance on starting the batch file.

The help output on running set /? in a command prompt window explains when and how to use delayed expansion on an IF and a FOR example. In code above delayed environment variable expansion is enabled and used to reference the current value of environment variable host on iterations of the loop body command block on which the environment variable is defined and gets assigned a string value.

There would be also possible to use:

@echo off
echo %DATE% %TIME%

setlocal EnableExtensions DisableDelayedExpansion
set "DNS1=8.8.8.8"
set "DNS2=8.8.4.4"

for %%i in (%DNS1% %DNS2%) do (
    echo ----------------
    if %%i == 8.8.8.8 set "host=Google1"
    if %%i == 8.8.4.4 set "host=Google2"
    call echo tracert to %%host%%
    tracert -d %%i
)

endlocal
echo %TIME%
exit /B

The command line call echo tracert to %%host%% is modified during the parsing step of entire command block by Windows command processor to call echo tracert to %host% and because of command CALL this command line is parsed a second time on each execution of the command block before ECHO command is executed resulting in printing the current value of environment variable host to console window.

But I suggest to use this much better code:

@echo off
echo %DATE% %TIME%
echo/

setlocal EnableExtensions DisableDelayedExpansion
set "DNS1=8.8.8.8=Google1"
set "DNS2=8.8.4.4=Google2"

for /F "tokens=2* delims==" %%I in ('set DNS 2^>nul') do (
    echo ----------------
    echo tracert to %%J
    echo %SystemRoot%\System32\tracert.exe -d %%I
)

endlocal
echo/
echo %TIME%
exit /B

One or more IP addresses and their host names are assigned to one or more environment variables starting with the string DNS. IP address and host name are separated by an equal sign.

The command FOR runs in a separate command process started with cmd.exe /C in background the command line:

set DNS 2>nul

Command SET outputs to handle STDOUT (standard output) all environment variables starting with DNS sorted alphabetically with name=value which means for this example:

DNS1=8.8.8.8=Google1
DNS2=8.8.4.4=Google2

The error message output by SET to handle STDERR (standard error) on not finding any environment variable starting with the string DNS would be suppressed by this code by redirecting it to device NUL.

Read also the Microsoft article 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.

FOR with using option /F captures everything output to handle STDOUT in background command process and processes this output by ignoring empty lines and lines starting with a semicolon.

The captured lines start all with DNS and so there is definitely no line ignored as no line starts with a ;.

FOR with using option /F would also split each line into substrings using space/tab as delimiter with assigning just first substring to specified loop variable I. But this line splitting behavior is not useful for this task.

For that reason the option string "tokens=2* delims==" redefines the line splitting behavior. Now = is used as delimiter between the strings instead of space and tab.

And instead of assigning the first = delimited string to loop variable I which would be the name of the environment variable, the second equal sign delimited string is assigned to loop variable I because of tokens=2 which is the IP address.

And there is additionally assigned to next loop variable J according to ASCII table the rest of the line after the equal sign(s) after second = delimited string without any further string splitting on an equal sign. So the host name is assigned to loop variable J, even on containing one or more = as long as not containing them at beginning of host name.

This code is obviously better as the DNS environment variables can be defined all at top of the batch file with IP address and host name and nothing must be changed on the command lines below because it simply processes from 0 to n environment variables starting with DNS and having at least a second = delimited substring.

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 /?
  • exit /?
  • for /?
  • if /?
  • set /?
  • setlocal /?
  • tracert /?

Upvotes: 1

Squashman
Squashman

Reputation: 14290

The proper use to test multiple conditions using an IF command is used in this code. As a best practice I always use quotes around string comparisons. Also note the use of delayed expansion with the host variable. This is required because you are creating a variable inside a parenthesized code block.

@echo off
setlocal enabledelayedexpansion

echo %date% %time%

SET DNS1=8.8.8.8
SET DNS2=8.8.4.4

for  %%i in (%DNS1% %DNS2%) do (
    echo ----------------
    if "%%i"=="8.8.8.8" set host=Google1
    if "%%i"=="8.8.4.4" set host=Google2
    echo tracert to !host!
    tracert -d %%i
)

echo %time%
endlocal
exit

Upvotes: 2

Related Questions