バッチ: FOR ループにおける特定の競合を回避する

バッチ: FOR ループにおける特定の競合を回避する

問題: FOR ループでの予期しない動作 (単一の重複)。

私は、ボリュームのマウントとマウント解除などのタスク (「Voltask」) を実行するためのシンプルなバッチ VBScript インターフェイスを作成しようとしています。関数の中心的な部分は、各ボリュームの一意の識別であり、これは 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 つのボリュームを表し、後者はボリュームに対応するデータのタイプを表します (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 は 2 回の実行ごとに増加し、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! として解釈され、最初の解析変数と 2 番目の解析変数の両方を参照するため、必ず競合が発生することがわかりました。この結果、重複が発生します。ただし、ジレンマは残ります。これをどうやって回避するか?

別のローカリゼーション レイヤーを設定すると、他のレイヤーを作成することを犠牲にして問題が解決しますが、実行可能なオプションではないようです。変数として数字の代わりに文字を使用することもできますが、これにより算術演算が壊れます。 *現在、shift を使用して set /a count+=1 を文字の同等のものに置き換えることを調査していますが、これまでのところ効果がありません。

答え1

最終的な変数名の 2 つの数字の間に区切り文字を入れるだけでコードを修正できます。ここではピリオドを使用します。

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

または、それぞれの名前の前にプレフィックスを付けることもできます。数字で始まる変数名は、 のようなバッチ引数と混同されるため、通常の展開では展開できないため、これは良いアイデアです。%1遅延展開を使用しているため、コードに問題はありません。ただし、数字で始まる変数は、一般的には避けるべきものです。

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

おそらく両方行うでしょう。変数が数字以外の何かで始まるのが好きですし、視覚的に 2 つの数字が分離されているのも好きです。

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

中間変数を使用せず、ループを 1 つだけ使用するようにコードを簡素化すると、技術的には変数名にプレフィックスや区切り文字は必要ありません。ループでは、次のように記述するだけで済みます。

  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

変数名の重複(競合)を解消するには、プレフィックスこれらを文字で置き換えます。たとえば、最初のパスではA1、、、、... を使用し、2番目のパスでは、、、、、、 ...A2を使用します。A3B11B12B21B22

このような状況では、変数名が統一されていないことが時々あります長さ は多少困難を伴います。(たとえば、A1...A9は 2 文字ですが、A10などは 3 文字です。) 私は、番号付けを 10 または 11、あるいは 100 または 101 から開始することで、この問題に対処することがあります。たとえば、 から開始する場合A10、入力行が 90 行あって、 までしか進むことができませんA99

関連情報