2

Problem: unexpected behavior (single duplicate) in FOR-loop.

I'm trying to make a simple batch-vbscript interface for mounting and dismounting volumes, among other tasks ("Voltask"). A central part of the function is unique identification of each volume, which i'm trying to achieve by parsing output from MOUNTVOL:

\\?\Volume{d35e9775-0176-11d6-a06d-806e6f6e6963}\
    H:\
\\?\Volume{d35e9776-0176-11d6-a06d-806e6f6e6963}\
    F:\
\\?\Volume{6537febd-01bc-11d6-adb5-806e6f6e6963}\
    *** NO MOUNT POINTS ***
\\?\Volume{3fb54500-a257-11e4-8d8d-806e6f6e6963}\
    *** NO MOUNT POINTS ***
\\?\Volume{6537febe-01bc-11d6-adb5-806e6f6e6963}\
    C:\
\\?\Volume{6537fec3-01bc-11d6-adb5-806e6f6e6963}\
    A:\
\\?\Volume{6537fec1-01bc-11d6-adb5-806e6f6e6963}\
    D:\
\\?\Volume{6537fec2-01bc-11d6-adb5-806e6f6e6963}\
    E:\

The first parse gives some subroutines what they need and makes the data generally more pleasing to handle. Each relevant line from MOUNTVOL is stored into a separate variable number.

@ECHO OFF & SETLOCAL enabledelayedexpansion & SET count=0
REM Basic parse
FOR /F "usebackq" %%5 IN (`
MOUNTVOL ^| FINDSTR /c:\ /c:*
`) DO (
SET /a count+=1
SET !count!=%%5
)

The next step is to take this simple output and move a step closer to combining corresponding letters and names: 1 2 3 4 5 -> 11 12 21 22 31... This way the first number would always represent one volume and the latter the type of data corresponding with the volume, 1=name, 2=letter.

I'm trying to achieve this by parsing the previously gained data with:

FOR /L %%G IN (1,1,%count%) DO (
SET /a ODD=%%G %%2
IF !ODD! EQU 1 (
SET A=%%G
SET /a A-=!C!
SET /a C+=1
SET B=1
) 
REM Intentionally not using "ELSE"
IF !ODD! EQU 0 (
SET A=%%G
SET /a A-=!C!
SET /a B+=1
)
SET !A!!B!=!%%G!
ECHO !A!!B! Has Data !%%G!
)

Just to clear up the arithmetics: the counts result in A increasing every second run and B varying between 1 and 2.

The resulting output as displayed is:

11 Has Data \\?\Volume{d35e9775-0176-11d6-a06d-806e6f6e6963}\
12 Has Data H:\
21 Has Data \\?\Volume{d35e9776-0176-11d6-a06d-806e6f6e6963}\
22 Has Data F:\
31 Has Data \\?\Volume{6537febd-01bc-11d6-adb5-806e6f6e6963}\
32 Has Data ***
41 Has Data \\?\Volume{3fb54500-a257-11e4-8d8d-806e6f6e6963}\
42 Has Data ***
51 Has Data \\?\Volume{6537febe-01bc-11d6-adb5-806e6f6e6963}\
52 Has Data C:\
61 Has Data \\?\Volume{d35e9775-0176-11d6-a06d-806e6f6e6963}\
62 Has Data H:\
71 Has Data \\?\Volume{6537fec1-01bc-11d6-adb5-806e6f6e6963}\
72 Has Data D:\
81 Has Data \\?\Volume{6537fec2-01bc-11d6-adb5-806e6f6e6963}\
82 Has Data E:\

61 and 62 being duplicates of 11 and 12. As merely a coincidence this would be curious as the values of %G at this exact point are also 11 and 12. This lead me to draw the conclusion that... i just don't see it.

Is !%%G! somehow being misinterpreted or did i make a mistake with the counts? I've banged my head on the screen for so long i'd really appreciate any tips!

EDIT: After a thorough break i realized that after %%G passes 10, conflicts are bound to happen as the !%%G! is interpreted as !11! and !12!, which refer to both first- and second-parse variables. This results in the duplicates. The dilemma, however, remains: how to avoid this?

Setting another layer of localization would solve the problem at the expense of creating others; does not seem to be a viable option. I could use letters instead of numbers as variables but this would break arithmetics. *Currently researching using shift to replace set /a count+=1 with an equivalent for letters, but so far to no avail.

3

You can fix your code simply by putting a delimiter between the two numbers in your final variable names - I'll use a period:

SET !A!.!B!=!%%G!
ECHO !A!.!B! Has Data !%%G!

Or you could put a prefix before each name. This is a good idea because you cannot expand a variable name beginning with a digit with normal expansion because it will be confused with a batch argument like %1. Not a problem with your code since you use delayed expansion. But variables beginning with a digit is something to generally avoid.

SET V!A!!B!=!%%G!
ECHO V!A!!B! Has Data !%%G!

I would probably do both. I like the variable beginning with something other than a digit, and visually I like the separation of the two numbers:

SET V!A!.!B!=!%%G!
ECHO V!A!.!B! Has Data !%%G!

Your code could be dramatically simplified:

@echo off
setlocal enableDelayedExpansion
set /a count=0
for /f %%A in ('mountvol ^| findstr /l "\ *"') do (
  set /a "A=count/2+1, B=count%%2+1, count+=1"
  set "V!A!.!B!=%%A"
  echo V!A!.!B!=%%A
)

Which should yield:

V1.1 Has Data \\?\Volume{d35e9775-0176-11d6-a06d-806e6f6e6963}\
V1.2 Has Data H:\
V2.1 Has Data \\?\Volume{d35e9776-0176-11d6-a06d-806e6f6e6963}\
V2.2 Has Data F:\
V3.1 Has Data \\?\Volume{6537febd-01bc-11d6-adb5-806e6f6e6963}\
V3.2 Has Data ***
V4.1 Has Data \\?\Volume{3fb54500-a257-11e4-8d8d-806e6f6e6963}\
V4.2 Has Data ***
V5.1 Has Data \\?\Volume{6537febe-01bc-11d6-adb5-806e6f6e6963}\
V5.2 Has Data C:\
V6.1 Has Data \\?\Volume{6537fec3-01bc-11d6-adb5-806e6f6e6963}\
V6.2 Has Data A:\
V7.1 Has Data \\?\Volume{6537fec1-01bc-11d6-adb5-806e6f6e6963}\
V7.2 Has Data D:\
V8.1 Has Data \\?\Volume{6537fec2-01bc-11d6-adb5-806e6f6e6963}\
V8.2 Has Data E:\

By simplifying the code to using only one loop, with no intermediate variables, there technically is no need for a prefix or delimiter in the variable name. You could simply use the following in my loop:

  set "!A!!B!=%%A"
  echo !A!!B! Has Data %%A

But I still would keep the prefix and the delimiter.

There may be 10 or more volumes on a machine - mine has 10. As Scott suggests, you can add 100 to the value to get a fixed width variable name that supports up to 99 volumes. I would also use a substring operation to get zero prefixed values, starting with 01.

@echo off
setlocal enableDelayedExpansion
set /a count=0
for /f %%A in ('mountvol ^| findstr /l "\ *"') do (
  set /a "A=count/2+101, B=count%%2+1, count+=1"
  set "V!A:~-2!.!B!=%%A"
  echo V!A:~-2!.!B!=%%A
)
  • All very good points; i'll be experimenting. Especially meaningful later on was pointing out the risks with %1. The first solution rivals my (or rather Scott's) other solution of replacing SET B=1 with SET B=10 in its simplicity. Thank you for the effort! – Alex Oja May 30 '15 at 23:56
1

Simply de-duplicate (deconflict) the variable names by prefixing them with letters.  For example, on the first pass, use A1, A2, A3, ..., and/or use B11, B12, B21, B22, ..., on the second pass.

In situations like this, I sometimes find that non-uniform variable name lengths causes some difficulty.  (For example, A1 ... A9 are two characters, but then A10, etc., are three characters.)  I sometimes handle that by starting the numbering at 10 or 11, or 100 or 101.  For example, if you start with A10, you can have 90 lines of input and only go up to A99.

  • I'm also trying the method of adding letters, just to learn some more, but the first problem with it is with the arithmetics - set /a should be able to work only with numbers. Your latter tip was very helpful and indeed constructive in terms of my deductive abilities. Simply replacing SET B=1 with SET B=10 achieved the goal. Thank you! – Alex Oja May 30 '15 at 23:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.