2

I write a batch file to store the error message using the if loops. But there is no result coming out from the index 0. I am feeling so weird about it and I have been scratching my head the whole day. Here is the code:

:loops
IF DEFINED result[%num1%] (
    REM call echo number %num1% %%result[!num1!]%%
    REm call echo Time    : %%result[!num1!]:~9,8%%
    REM call echo Value  : %num1%
    REM call SET timeInLog[!num1!]=%%result[!num1!]:~9,8%%

    call SET nHour=%%result[!num1!]:~9,2%%
    call SET nMins=%%result[!num1!]:~12,2%%
    call SET nSec=%%result[!num1!]:~15,2%%
    call SET timeinlog=%nHour%%nMins%%nSec%
    call echo %num1% and time : !timeinlog!
    SET /A num1+=1
    goto :loops
)
pause

The result is:

0 and time :
1 and time : 090747
2 and time : 112418
3 and time : 121641
4 and time : 181427
Press any key to continue . . .

Update: result[num] is an array which store some string from my filename.txt. And yes, SET num1=0 has been defined in above the loop

Sample String is 00242274 00:02:04 [13064] Current Control - HotSeasonalEvent

Kopi Bryant
  • 1,139
  • 12
  • 26

1 Answers1

0

I first recommend to read the following two questions with their answers:

Every environment variable reference with %VariableName% is replaced by Windows command processor on a command line or an entire command block as used here before execution of the command line respectively the command making use of the command block.

It can be seen on debugging the batch file that the entire command block is processed already to following command lines before running the condition the first time:

IF DEFINED result[0] (
REM call echo number 0 %result[!num1!]%
 REm call echo Time    : %result[!num1!]:~9,8%
 REM call echo Value  : 0
 REM call SET timeInLog[!num1!]=%result[!num1!]:~9,8%
 call SET nHour=%result[!num1!]:~9,2%
 call SET nMins=%result[!num1!]:~12,2%
 call SET nSec=%result[!num1!]:~15,2%
 call SET timeinlog=
 call echo 0 and time : !timeinlog!
 SET /A num1+=1
 goto :loops
)

So all occurrences of %% have been processed to just %. The command call results in processing the command lines a second time which define the environment variables nHour, nMins and nSec before executing the command set. So these three environment variables are later correct defined if the IF condition is true.

But let us look on the two other command lines with command call:

call SET timeinlog=%nHour%%nMins%%nSec%
call echo %num1% and time : !timeinlog!

%% are not used around the environment variable names nHour, nMins, nSec and num1. For that reason cmd.exe replaces those environment variable references already on parsing entire command block with the current values of the three environment variables before running the command IF. The environment variable num1 is defined already with value 0 and so %num1% in second line is replaced by the expected 0. But the environment variables nHour, nMins and nSec are not defined at all on first parsing of command block. So their references are replaced by nothing and remaining is call SET timeinlog= on first run of the command block. This line deletes environment variable timeinlog instead of defining it with a concatenation of the values of the three environment variables executed before.

On second parsing of entire command block after execution of goto :loops, the environment variables nHour, nMins and nSec are defined now with the values of the first execution. So there is defined timeinlog for result[1] with the hour, minute and second of result[0] from first run. That is not the wanted behavior.

One solution is using delayed expansion right:

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "num1=0"
set "result[0]=00242274  00:02:04    [13064] Current Control - HotSeasonalEvent"

:loops
if defined result[%num1%] (
    set nHour=!result[%num1%]:~10,2!
    set nMins=!result[%num1%]:~13,2!
    set nSec=!result[%num1%]:~16,2!
    set timeinlog=!nHour!!nMins!!nSec!
    echo %num1% and time : !timeinlog!
    set /A num1+=1
    goto loops
)

endlocal
pause

num1 is the environment variable of which value is just modified at end of the command block. So it is better to reference this environment variable with immediate expansion on parsing entire command block and reference with delayed environment variable expansion all other environment variables.

Another solution is using a FOR loop:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "result[0]=00242274  00:02:04    [13064] Current Control - HotSeasonalEvent 1"
set "result[1]=00242275     00:02:12    [13065] Current Control - HotSeasonalEvent 2"
set "result[2]=00242276 00:05:23    [13066] Current Control - HotSeasonalEvent 3"

for /F "tokens=2-5* delims= =: " %%I in ('set result[ 2^>nul') do (
    echo/
    echo Number is: %%I
    echo   Time is: %%J%%K%%L
    echo  Value is: %%M
)

endlocal
echo/
pause

ATTENTION: There must be a horizontal tab character between the two equal signs after delims on FOR command line. There is a normal space character after the colon on same command line.

The command FOR with option /F starts one more command process in background with %ComSpec% /c and the command line specified between '. This results with Windows installed in C:\Windows in execution of:

C:\Windows\System32\cmd.exe /c set result[ 2>nul

The command SET outputs in background command process to handle STDOUT all environment variables of which name starts with result[ with using the syntax VariableName=VariableValue. This output is captured by the command process running the batch file with command FOR.

It could be that there is no environment variable of which name starts with result[. In this case the command SET outputs an error message to handle STDERR of background command process which would be redirected to handle STDERR of the command process running command FOR resulting in getting it displayed in console window. The error message is suppressed by using 2>nul to redirect STDERR of background command process to device NUL.

Read 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 in a separate command process started in background.

After started cmd.exe terminated itself, all the captured output to its STDOUT is processed by FOR line by line.

Empty lines are skipped by FOR, but such lines do not occur here.

Next a line is split up into substrings. Normal space and horizontal tab are used by default to split up a line. In this case two more substring delimiters are needed to get the wanted output, the equal sign and the colon. For that reason delims= =:  redefines the delimiters to a horizontal tab, an equal sign, a colon and a normal space.

The command FOR looks on first character of first substring after splitting up the line using the specified delimiters if this character is the end of line character which is by default ;. The line would be ignored by FOR if the first substring after splitting up the line would start with a semicolon. This cannot happen here as all captured lines processed by FOR start with result[.

FOR would assign by default just first substring to the specified loop variable I which is not wanted here. More substrings are of interest for the task. For that reason tokens=2-5* is used to split up for example the line

result[0]=00242274  00:02:04    [13064] Current Control - HotSeasonalEvent 1

into the substrings:

  1. result[0]
  2. 00242274
  3. 00
  4. 02
  5. 04
  6. [13064] Current Control - HotSeasonalEvent 1.

The first substring is not of interest and for that reason ignored. The substrings 2 to 5 are of interest for the task. The second substring is assigned to loop variable I. The third substring is assigned to next variable after I according to the ASCII table which is in this case J. The fourth substring is assigned to next but one variable after I which is K and so on for the remaining substrings. Now it should be clear why the loop variables are interpreted case-sensitive by Windows command processor. The asterisk after 5 in option string tokens=2-5* means that after fifth tab/equal sign/colon/space delimited substring the remaining line should be not further split up. In this case the sixth substring containing spaces is assigned to loop variable M.

The output of this batch file is:

Number is: 00242274
  Time is: 000204
 Value is: [13064] Current Control - HotSeasonalEvent 1

Number is: 00242275
  Time is: 000212
 Value is: [13065] Current Control - HotSeasonalEvent 2

Number is: 00242276
  Time is: 000523
 Value is: [13066] Current Control - HotSeasonalEvent 3

It would be of course possible to use directly the FOR loop on the text file which was parsed before to define the environment variables starting with result[.

Mofi
  • 38,783
  • 14
  • 62
  • 115