Пакет: избегание определенных конфликтов в циклах FOR

Пакет: избегание определенных конфликтов в циклах FOR

Проблема: неожиданное поведение (единственный дубликат) в цикле FOR.

Я пытаюсь создать простой интерфейс пакетного vbscript для монтирования и размонтирования томов, среди прочих задач («Voltask»). Центральная часть функции — уникальная идентификация каждого тома, чего я пытаюсь добиться путем анализа вывода из 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:\

Первый парсинг дает некоторым подпрограммам то, что им нужно, и делает данные в целом более приятными для обработки. Каждая соответствующая строка из MOUNTVOL сохраняется в отдельный номер переменной.

@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
)

Следующий шаг — взять этот простой вывод и приблизиться к объединению соответствующих букв и имен: 1 2 3 4 5 -> 11 12 21 22 31... Таким образом, первое число всегда будет представлять один том, а второе — тип данных, соответствующих тому, 1=имя, 2=буква.

Я пытаюсь добиться этого путем анализа ранее полученных данных с помощью:

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!
)

Просто для ясности арифметики: подсчеты показывают, что A увеличивается с каждым вторым запуском, а B колеблется от 1 до 2.

Полученный результат выглядит следующим образом:

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 и 62 являются дубликатами 11 и 12. Как простое совпадение это было бы любопытно, поскольку значения %G в этой самой точке также равны 11 и 12. Это привело меня к выводу, что... я просто этого не вижу.

!%%G! как-то неправильно истолковывается или я ошибся с подсчетами? Я так долго бился головой об экран, что буду очень признателен за любые советы!

EDIT: После основательного перерыва я понял, что после того, как %%G превысит 10, конфликты обязательно возникнут, так как !%%G! интерпретируется как !11! и !12!, которые относятся как к переменным первого, так и второго разбора. Это приводит к дубликатам. Однако дилемма остается: как этого избежать?

Установка еще одного уровня локализации решила бы проблему за счет создания других; не кажется жизнеспособным вариантом. Я мог бы использовать буквы вместо цифр в качестве переменных, но это нарушило бы арифметику. *В настоящее время исследую использование shift для замены set /a count+=1 на эквивалент для букв, но пока безрезультатно.

решение1

Вы можете исправить свой код, просто поставив разделитель между двумя числами в ваших окончательных именах переменных — я буду использовать точку:

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

Или вы можете поставить префикс перед каждым именем. Это хорошая идея, потому что вы не можете расширить имя переменной, начинающееся с цифры, с помощью обычного расширения, потому что оно будет спутано с аргументом пакета, например %1. Это не проблема для вашего кода, так как вы используете отложенное расширение. Но переменные, начинающиеся с цифры, — это то, чего обычно следует избегать.

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

Я бы, наверное, сделал оба варианта. Мне нравится переменная, начинающаяся не с цифры, а с визуальной точки зрения, и мне нравится разделение двух чисел:

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

Ваш код можно значительно упростить:

@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
)

Что должно дать:

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:\

Упрощая код до использования только одного цикла, без промежуточных переменных, технически нет необходимости в префиксе или разделителе в имени переменной. Вы можете просто использовать следующее в моем цикле:

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

Но я бы все равно сохранил префикс и разделитель.

На машине может быть 10 или более томов — у меня их 10. Как предлагает Скотт, можно добавить 100 к значению, чтобы получить имя переменной фиксированной ширины, поддерживающее до 99 томов. Я бы также использовал операцию подстроки, чтобы получить нулевые префиксные значения, начинающиеся с 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
)

решение2

Просто устраните дублирование (конфликт) имен переменныхпрефиксих буквами. Например, при первом проходе используйте A1, A2, A3, ..., и/или используйте B11, B12, B21, B22, ..., при втором проходе.

В таких ситуациях я иногда обнаруживаю, что нестандартное имя переменнойдлины вызывает некоторые трудности. (Например, A1... A9— это два символа, но затем A10и т. д. — это три символа.) Иногда я справляюсь с этим, начиная нумерацию с 10 или 11, или 100, или 101. Например, если вы начнете с A10, у вас может быть 90 строк ввода и дойти только до A99.

Связанный контент