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 with using 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:
result[0]
00242274
00
02
04
[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[
.