FOR loop does not increment /L %%parameter

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

FOR loop does not increment /L %%parameter

#1 Post by BoQsc » 23 Sep 2018 08:24

Can someone point out why incrementation do not work, and how to fix it?

The part where incrementation do no occur:
EndLocal & FOR /L %%i in (0, 1, 2) DO ( echo %%i - Do not increment???
REM Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
REM Set "VolumeName[%%i]=%VolumeName[%%i]: =%"

)

Full code

Code: Select all

@Echo off

call :searchUSBdriveLetter
echo %DeviceID[]%

::echo "%USBdriveLetter%"

:: Set "installDevice=\\?\%USBdriveLetter%"
:: echo %installDevice% - device

pause

:searchUSBdriveLetter
setlocal enabledelayedexpansion
	Set "USBdriveLetter="
	Set /a "i=0"
	For /f "tokens=1-9" %%a IN (
	'wmic logicaldisk list brief'
	) do (
		Set "DeviceID=%%a"
		Set "DriveType=%%b"
		Set "VolumeName=%%e"
		Set "ProviderName=%%d"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set /a "i=i+1"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "DeviceID[!i!]=!DeviceID!"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "VolumeName[!i!]=!VolumeName!"		
	)
	Set "countedUSBdrives=%i%"
	
	
	IF ["%USBdriveLetter%"]==[""] Set "USBdriveLetter=NoConnectedUSBdriveLetter" 
EndLocal & FOR /L %%i in (0, 1, 2) DO ( echo %%i - Do not increment??? 
	REM Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
	REM Set "VolumeName[%%i]=%VolumeName[%%i]: =%"

) & GOTO :Eof






Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: FOR loop does not increment /L %%parameter

#2 Post by Ed Dyreen » 23 Sep 2018 08:52

Your for loop ends with goto :EOF thus telling to abort the for loop and leave the function. Similar as when you would write return inside a java program the function is exited immediately commands behind it will not be executed, loops aborted.

You probably expect for x in ( set ) do ( commands ) &command will only iterate the block, this is not the case. for assumes you want to loop block & command.

In this case proper syntax to make it do what you expect is ( for x in ( set ) do commands ) &command.

BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

Re: FOR loop does not increment /L %%parameter

#3 Post by BoQsc » 23 Sep 2018 09:30

Ed Dyreen wrote:
23 Sep 2018 08:52
Your for loop ends with goto :EOF thus telling to abort the for loop and leave the function. Similar as when you would write return inside a java program the function is exited immediately commands behind it will not be executed, loops aborted.

You probably expect for x in ( set ) do ( commands ) &command will only iterate the block, this is not the case. for assumes you want to loop block & command.

In this case proper syntax to make it do what you expect is ( for x in ( set ) do commands ) &command.
Thank you, it was very helpful.

But I still unable to loop through, does that mean I need EnableDelayedExpansion for the Loop and escape variable with exclamation marks?
EndLocal & (
FOR /L %%i in (0, 1, 2) DO (
Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
Set "VolumeName[%%i]=%VolumeName[%%i]: =%"

Echo %%i - Finally Iterates!
Echo %DeviceID[1]% - Manual index placement works perfectly, returns Device ID
Echo %DeviceID[%%i]% - ECHO is off. ???
)
) & GOTO :EOF

Full current code

Code: Select all

@Echo off

call :searchUSBdriveLetters
pause
REM Finally getting the array from the function
REM echo %DeviceID[1]%
REM echo %DeviceID[2]%
REM echo %DeviceID[3]%

REM echo %VolumeName[1]%
REM echo %VolumeName[2]%
REM echo %VolumeName[3]%

:searchUSBdriveLetters
setlocal enabledelayedexpansion
	
	Set /a "i=0"
	For /f "tokens=1-9" %%a IN (
	'wmic logicaldisk list brief'
	) do (
		Set "DeviceID=%%a"
		Set "DriveType=%%b"
		Set "VolumeName=%%e"
		Set "ProviderName=%%d"
		
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "DeviceID[!i!]=!DeviceID!"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "VolumeName[!i!]=!VolumeName!"		
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set /a "i=i+1"
	)
	Set "countedUSBdrives=%i%"
	
	
	REM IF ["%USBdriveLetter%"]==[""] Set "USBdriveLetter=NoConnectedUSBdriveLetter" 
EndLocal & ( 
	FOR /L %%i in (0, 1, 2) DO ( 
		Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
		Set "VolumeName[%%i]=%VolumeName[%%i]: =%"
		
		Echo %%i - Finally Iterates!
		Echo %DeviceID[1]% -  Manual index placement works perfectly, returns Device ID
		Echo %DeviceID[%%i]% -  ECHO is off. ???
	) 
) & GOTO :EOF








Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: FOR loop does not increment /L %%parameter

#4 Post by Ed Dyreen » 23 Sep 2018 12:41

Cmd assumes you mean %variable%%variable%, which results empty, thus you get Echo which will display ECHO is ...

You want cmd to delay the expansion of the most outer variable. This can be done using call
set a=b
set bbb=3
call echo.%%b%a%b%%

Or with delayed expansion
setlocal enableDelayedExpansion
set a=b
set bbb=3
echo.!b%a%b!

You have a similar problem here:
Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
Set "VolumeName[%%i]=%VolumeName[%%i]: =%"

You say "Echo %DeviceID[1]% - Manual index placement works perfectly, returns Device ID"

But what really happens is
Set "DeviceID[x]=%x%%x%"
Set "DeviceID[x]="

You've been mislead by the fact that you are using immediate expansion inside a block. So you'll get the unchanged value of DeviceID[x] inside the block but once you leave the block you'll find that DeviceID[x] is empty.

If you replace Echo %DeviceID[x]% with set DeviceID[x] you can test the current value and find that DeviceID[x] is empty.

BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

Re: FOR loop does not increment /L %%parameter

#5 Post by BoQsc » 23 Sep 2018 13:52

Ed Dyreen wrote:
23 Sep 2018 12:41
You have a similar problem here:
Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
Set "VolumeName[%%i]=%VolumeName[%%i]: =%"

You say "Echo %DeviceID[1]% - Manual index placement works perfectly, returns Device ID"

But what really happens is
Set "DeviceID[x]=%x%%x%"
Set "DeviceID[x]="

You've been mislead by the fact that you are using immediate expansion inside a block. So you'll get the unchanged value of DeviceID[x] inside the block but once you leave the block you'll find that DeviceID[x] is empty.

If you replace Echo %DeviceID[x]% with set DeviceID[x] you can test the current value and find that DeviceID[x] is empty.
You are right, tested and the variable is actually empty.

Ed Dyreen wrote:
23 Sep 2018 12:41

Cmd assumes you mean %variable%%variable%, which results empty, thus you get Echo which will display ECHO is ...

You want cmd to delay the expansion of the most outer variable. This can be done using call
set a=b
set bbb=3
call echo.%%b%a%b%%

Or with delayed expansion
setlocal enableDelayedExpansion
set a=b
set bbb=3
echo.!b%a%b!
I do not completely understand where to correctly apply delay of expansion using a call command. As I'm still getting the same empty ...

Code: Select all

EndLocal & ( 
	FOR /L %%i in (0, 1, 2) DO ( 
	
		Echo %%i - Finally Iterates!
		Echo %DeviceID[1]% -  Manual index placement, returns Device ID
		call Echo %%DeviceID[%%i]%% - Still empty.
		
		
		call Set "DeviceID[%%i]=%%DeviceID[%%i] =%%"
		call Set "VolumeName[%%i]=%%VolumeName[%%i] =%%"
		
		

	) 
) & GOTO :EOF


Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: FOR loop does not increment /L %%parameter

#6 Post by Ed Dyreen » 23 Sep 2018 14:33

Code: Select all

@echo off

Set "DeviceID[0]=XXXX XXXX"

FOR /L %%i in (0,1,0) DO ( 
	
	Echo %%i - Finally Iterates!
	Echo %DeviceID[0]% -  Manual index placement, returns Device ID
	call Echo %%DeviceID[%%i]%% - Still empty.
	Set "DeviceID[0]

	call Set "DeviceID[%%i]=%%DeviceID[%%i] =%%"
	Set "DeviceID[0]
) 

pause

Code: Select all

0 - Finally Iterates!
XXXX XXXX -  Manual index placement, returns Device ID
XXXX XXXX - Still empty.
DeviceID[0]=XXXX XXXX
Omgevingsvariabele DeviceID[0] is niet gedefinieerd
Druk op een toets om door te gaan. . .
XXXX XXXX is not empty where you say Still empty.

But becomes empty when executing
call Set "DeviceID[%%i]=%%DeviceID[%%i] =%%"

Because now you are saying:
call Set "DeviceID[%%i]=%%DeviceID[%%i] =%%"
Set "DeviceID[0]=%DeviceID[0] =%"
Set "DeviceID[0]="

Correct syntax is set "x=%x: =y%" or when inside a block call set "x=%%x: =y%%" or set "x=!x: =y!"

In your case that translates to call Set "DeviceID[%%i]=%%DeviceID[%%i]: =%%"
or set "DeviceID[%%i]=!DeviceID[%%i]: =!" if you prefer delayed expansion.

BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

Re: FOR loop does not increment /L %%parameter

#7 Post by BoQsc » 24 Sep 2018 00:18

Code: Select all

EndLocal & ( 
	FOR /L %%i in (0, 1, 2) DO ( 
	
		Echo %%i - Finally Iterates!
		Echo %DeviceID[1]% -  Manual index placement works perfectly, returns Device ID
		call Echo %%DeviceID[%%i]%% - Still becomes empty. ❗
		Echo %DeviceID[%%i]% - Still becomes empty. ❗
		
		REM commented both of them, still becomes empty regardless of the below commands. ❗
		REM call Set "DeviceID[%%i]=%%DeviceID[%%i]: =%%"
		REM call Set "VolumeName[%%i]=%%VolumeName[%%i]: =%%"
		
		

	) 
) & GOTO :EOF

That doesn't make the script part work.

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: FOR loop does not increment /L %%parameter

#8 Post by Ed Dyreen » 24 Sep 2018 05:25

Copy the code from my previous post, run it, it should give the same results I posted.

From there you go along and figure out what you do that makes the code not working.

If you do not know why, post the complete code.

Not the code you have but testCode that we can actually work with.

What I mean is, partial codes can only be tested for syntax and are not helpful otherwise.

Initialise test variables like I did.

Replace the first part with test variables like I did.

Otherwise you are making it difficult for others, we don't have your machine config.

So your batch begins like.

Code: Select all

@echo off

Set "DeviceID[0]=XXXX XXXX"

FOR /L %%i in (0,1,0) DO (
Use a problem solving unitTesting mindset.

Evidence based, prove that it doesn't work, so we can replicate the problem.

Lastly, read our sticky ( the first post in this forum ) by foxidrive.

BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

Re: FOR loop does not increment /L %%parameter

#9 Post by BoQsc » 24 Sep 2018 08:34

What this script suppose to do: Return all connected USB drives, so that I could use Gdisk.exe to manipulate, create partitions and so on.
Gdisk.exe part is not written yet as I'm stuck with iteration as mentioned on the subject of this thread.


The requirements to run the code are simple: The WMI command-line (WMIC) utility provides a command-line interface for WMI.
WMIC is enabled by default on Windows 7, Windows 8, Windows 8.1, Windows 10.
The only exceptions: Windows XP and below or hacker edited versions.

Everything else is the same and could be ran even on the early Windows xp.

It's a simple script, this script is everything that there is:

Code: Select all

@Echo off

call :searchUSBdriveLetters
pause

:searchUSBdriveLetters
setlocal enabledelayedexpansion
	
	Set /a "i=0"
	For /f "tokens=1-9" %%a IN (
	'wmic logicaldisk list brief'
	) do (
		Set "DeviceID=%%a"
		Set "DriveType=%%b"
		Set "VolumeName=%%e"
		Set "ProviderName=%%d"
		
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "DeviceID[!i!]=!DeviceID!"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "VolumeName[!i!]=!VolumeName!"		
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set /a "i=i+1"
	)
	Set "countedUSBdrives=%i%"
	
EndLocal & ( 
	FOR /L %%i in (0, 1, %countedUSBdrives%) DO ( 
		Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
		Set "VolumeName[%%i]=%VolumeName[%%i]: =%"
		
		Echo %%i - Finally Iterates!
		Echo %DeviceID[0]% -  Manual index placement, returns Device ID
		Echo %DeviceID[%%i]%
	) 
) & GOTO :EOF
How to to use the script:
  • Create new .bat or .cmd file
  • Copy the script that I mentioned above
  • Connect Any USB usb flash drive
  • Run the script from the Desktop

See the results:

Code: Select all

0 - Finally Iterates!
E: -  Manual index placement, returns Device ID
ECHO is off.
1 - Finally Iterates!
E: -  Manual index placement, returns Device ID
ECHO is off.
Press any key to continue . . .

Notice that ECHO is off.
Notice that instead of it, should be: some drive letter
For me it is E:

That is what this whole thread is all about.
That is the problem.

ECHO is off. instead of E:

I tried what I could understand, but that didn't solve the problem.
So, I need someone to point out the obvious that is not obvious to me.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: FOR loop does not increment /L %%parameter

#10 Post by dbenham » 24 Sep 2018 10:17

I see 4 serious bugs that I annotated in your code below (see BUG comments):

Code: Select all

@Echo off

call :searchUSBdriveLetters
pause
:: BUG-Falls into search routine. Need EXIT /B or GOTO :EOF to fix =%

:searchUSBdriveLetters
setlocal enabledelayedexpansion

	Set /a "i=0"
	For /f "tokens=1-9" %%a IN (
	'wmic logicaldisk list brief'
	) do (
		Set "DeviceID=%%a"
		Set "DriveType=%%b"
		Set "VolumeName=%%e"
		Set "ProviderName=%%d"

                %= BUG- ProviderName is a string representing network path. Comparing to 0 makes no sense =%
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "DeviceID[!i!]=!DeviceID!"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set "VolumeName[!i!]=!VolumeName!"
		if !DriveType! Equ 2 if !ProviderName! gtr 0 Set /a "i=i+1"
	)
	Set "countedUSBdrives=%i%"

EndLocal & (
	FOR /L %%i in (0, 1, %countedUSBdrives%) DO (
		Set "DeviceID[%%i]=%DeviceID[%%i]: =%"
		Set "VolumeName[%%i]=%VolumeName[%%i]: =%"  %= BUG-Cannot work because FOR var expanded after env var expansion =%

		Echo %%i - Finally Iterates!
		Echo %DeviceID[0]% -  Manual index placement, returns Device ID
		Echo %DeviceID[%%i]%
	)
) & GOTO :EOF    %= BUG-This kills loop because gets executed with iteration - NOT after loop is complete =%
And here is an expanded summary of bugs:
  • GOTO :EOF kills your loop prematurely, which makes it look like loop is not iterating (This answers the Why part of your question)
  • Missing EXIT /B before your subroutine, so it gets executed twice, once by CALL, and again by falling through
  • ProviderName is a string value representing network path to logical device. Comparing to 0 makes no sense
  • FOR variables are expanded after environment variable percent expansion, so %VolumeName[%%i]: =% cannot work
  • VolumeName can have spaces, resulting in value spread across multiple tokens.
  • I think ProviderName can have spaces, which would make it impossible to know where VolumeName begins
  • Last retrieved token when using WMIC with FOR /F will end with 0x0D Carriage Return due to quirky bug in cmd.exe. This could cause a problem, depending on how token is used.
I would completely rewrite the code using the following techniques:
  • Use WMIC WHERE clause to filter the records - avoids need for IF
  • Use WMIC GET to retrieve just the values that are needed. Note that unquoted commas must be escaped within FOR /F IN() clause
  • Use FINDSTR to further eliminate unwanted rows
  • Use 2nd FINDSTR to add line number prefix to each line, so don't need to increment a line counter
  • Use 2nd FOR /F to eliminate unwanted trailing 0x0D
  • Use FOR variables directly. Don't transfer to intermediate environment variable if not needed
  • Delayed expansion is no longer needed to get values, nor is a subroutine needed with temp variables
  • Use DELIMS=:<space> at end so can parse out FINDSTR line number as well as WMIC value tokens
  • Use TOKENS=1,2* so last token 3 can contain multiple space delimited words
Note that I eliminated the erroneous ProviderName test, but I don't know what to replace it with.

Code: Select all

@echo off
setlocal

:: Initialize driveCount to 0 in case no drives found
set "driveCount=0"  REM Initialize to 0 in case no drives found

:: Get the array of USB drives
for /f "delims=" %%L in (
  'wmic logicalDisk where "DriveType=2" get DeviceID^, VolumeName 2^>nul ^| findstr "^.:" ^| findstr /n "^"'
) do for /f "tokens=1,2* delims=: " %%A in ("%%L") do (
  set "driveCount=%%A"
  set "drive[%%A]=%%B:"
  set "name[%%A]=%%C"
)

:: List the results
setlocal enableDelayedExpansion
if %driveCount% equ 0 echo No USB drives found.
for /l %%N in (1 1 %driveCount%) do echo !drive[%%N]! = !name[%%N]!
exit /b
If you wanted to add back the nonsensical ProviderName test, then the where clause would be WHERE "DriveType=2 and ProviderName>0"
WMIC uses SQL syntax for the WHERE clause. Enclosing the entire clause in double quotes avoids a number of escape issues. String values should be enclosed within single quotes when used in a WHERE clause.
Dave Benham

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: FOR loop does not increment /L %%parameter

#11 Post by Ed Dyreen » 24 Sep 2018 12:23

floppydisks are also drivetype 2 so you are really searching for any removableDrives.

BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

Re: FOR loop does not increment /L %%parameter

#12 Post by BoQsc » 24 Sep 2018 13:50

Ed Dyreen wrote:
24 Sep 2018 12:23
floppydisks are also drivetype 2 so you are really searching for any removableDrives.
I'm searching only for USB Flash devices, and my own portable HDD is not getting considered as drive type 2. Kind of Strange.
That would be great to come up with general easily extendable scripts such as in this thread and upload into Github/Gitlab for others to look up and use what looks interesting.

For a second i did though about writting everything in Java instead, as batch has no short concise guidelines on precisely understanding how to write code without getting into troubles.
Also, there is no way known way to easily copy files with progress bar without utility or hardcore batching. I need to copy files after the USB is found but xcopy, copy, robocopy do not have overall progression bar to show what percentage has been completed, which kind of annoying to not know how long it might take for copying task to be completed

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: FOR loop does not increment /L %%parameter

#13 Post by aGerman » 24 Sep 2018 14:21

I didn't read the whole thread ...
BoQsc wrote:
24 Sep 2018 13:50
I'm searching only for USB Flash devices
What about

Code: Select all

for /f tokens^=2^ delims^=^" %%i in (
  'WMIC Path Win32_DiskDrive Where "InterfaceType='USB'" Assoc /assocclass:Win32_DiskDriveToDiskPartition 2^>nul ^|findstr /c:"Disk #"'
) do for /f tokens^=4^ delims^=^" %%j in ('WMIC Path Win32_LogicalDiskToPartition 2^>nul ^|findstr /c:"%%i"') do echo %%j
Steffen

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: FOR loop does not increment /L %%parameter

#14 Post by Ed Dyreen » 24 Sep 2018 19:00

BoQsc wrote:
24 Sep 2018 13:50
For a second i did though about writting everything in Java instead, as batch has no short concise guidelines on precisely understanding how to write code without getting into troubles.
Java is really not designed for system programming, you will need additional libraries and it is one of the most complex languages in existence. VBScript on the other hand is designed for system programming, easy to learn, no batch like quirks, object oriented, very fast compared to batch and easy to learn.
aGerman wrote:
24 Sep 2018 14:21
What about

Code: Select all

for /f tokens^=2^ delims^=^" %%i in (
  'WMIC Path Win32_DiskDrive Where "InterfaceType='USB'" Assoc /assocclass:Win32_DiskDriveToDiskPartition 2^>nul ^|findstr /c:"Disk #"'
) do for /f tokens^=4^ delims^=^" %%j in ('WMIC Path Win32_LogicalDiskToPartition 2^>nul ^|findstr /c:"%%i"') do echo %%j
Steffen
I am noting this down, I didn't know you could do associations through batch, great :D
Do you know of an online microsoft document that explains in detail all these wmic sql dialect options ?
These things are not found in wmic help unfortunately.

BoQsc
Posts: 92
Joined: 30 Jun 2014 04:10

Re: FOR loop does not increment /L %%parameter

#15 Post by BoQsc » 25 Sep 2018 00:42

Ed Dyreen wrote:
24 Sep 2018 19:00
BoQsc wrote:
24 Sep 2018 13:50
For a second i did though about writting everything in Java instead, as batch has no short concise guidelines on precisely understanding how to write code without getting into troubles.
Java is really not designed for system programming, you will need additional libraries and it is one of the most complex languages in existence. VBScript on the other hand is designed for system programming, easy to learn, no batch like quirks, object oriented, very fast compared to batch and easy to learn.
This is what I've found using google by searching around google:
VBScript doesn't have a progress bar, and it would be difficult to multithread so that you could be copying a file and simultaneously looking at how much had been copied. I wonder if there's a way to invoke the "flying document" bar from Windows Explorer. It doesn't show progress, but at least it indicates that something is happening. Hmmm...
https://forums.windowssecrets.com/showt ... post279670

This is complete solution in Java that I've found even faster and it looks even more readable than VBS, at least from the first look.
https://stackoverflow.com/a/13576203/3789797
you will need additional libraries and it is one of the most complex languages in existence.
Basics are pretty strightforward, might take few try hard months to become at least basic level software architect.
And Java seems to give as much complexity as you want, or as needed. The only hate so far - it needs runtime to be installed which is at least 20-200mb
Although it might run on linux and apple computers, with little tweaks, I guess.

Anyways I might use wmic even in Java, as it seems to be trustworthy native tool for Windows.

Post Reply