Проблема: неожиданное поведение (единственный дубликат) в цикле 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
.