批处理:避免 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! 是不是被误解了,还是我计算错了?我在屏幕上花了很长时间,如果能得到任何提示我真的很感激!

编辑:经过彻底的思考,我意识到 %%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 个。正如 Scott 所建议的,您可以将 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

只需通过以下方式删除重复的变量名(消除冲突)即可前缀用字母替换它们。例如,在第一遍中,使用A1A2A3、 ...,和/或在第二遍中使用B11B12B21、 、 ...。B22

在这种情况下,我有时会发现不统一的变量名长度 会造成一些困难。(例如,A1...A9是两个字符,但 等A10是三个字符。)我有时会通过从 10 或 11 或 100 或 101 开始编号来处理这种情况。例如,如果从 开始A10,则可以有 90 行输入,最多只能到A99

相关内容