Excel VBAを利用したファイル名の大文字・小文字一括変換方法では、folder内mappingを作ってcollisionを全件検査し、Approvedの一件ごとにtemporary path経由でcaseを変更する。Windowsの一般的なcase-insensitive filesystemではcaseだけのName変更が直接反映されないことがあり、一意なtemporary名を経由する。UnicodeのLCase/UCaseはlocaleや文字で一対一でない場合もある。この記事はold path、desired case path、temporary path、binary comparison、approval statusをmapにするを判断軸にして、記事固有のコード、合否、停止条件、復元を順序立てて説明します。
内容やtagを変えず、base nameの大文字・小文字表記だけを安全に変換する。完了は「全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である」です。結果が空なら「oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない」として調べ、エラーを0件へ置き換えません。
case-insensitive filesystemを考慮
folder内mappingを作ってcollisionを全件検査し、Approvedの一件ごとにtemporary path経由でcaseを変更する。ファイル名の大小文字だけを一括変換では、単にコマンドが終了したことではなく「全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である」を完了条件にします。内容やtagを変えず、base nameの大文字・小文字表記だけを安全に変換する。
case-insensitive filesystemを考慮に入る前に、対象、実行場所、権限、入力の由来を確認します。oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない。判定不能を成功へ丸めません。
folder内のold→new mapをpreview
folder内のold→new mapをpreviewでは「old path、desired case path、temporary path、binary comparison、approval statusをmapにする」という粒度で対象を特定します。Windowsの一般的なcase-insensitive filesystemではcaseだけのName変更が直接反映されないことがあり、一意なtemporary名を経由する。UnicodeのLCase/UCaseはlocaleや文字で一対一でない場合もある。表示名や先頭候補だけを採用しません。
ファイル名の大小文字だけを一括変換の対象が複数なら、候補数と除外理由を残します。ファイル名の大小文字だけを一括変換では実行ユーザー、OS・製品版、locale、カレントディレクトリも結果の解釈へ影響するため同時に記録します。
変換後名のcollisionを事前検査
変換後名のcollisionを事前検査は変更や出力生成より先に行う観測です。old path、desired case path、temporary path、binary comparison、approval statusをmapにするを含む形で現状を保存し、後段のコードが同じ対象へ向くか確認します。
Option Explicit
Private Type CaseRenameEntry
SheetRow As Long
OldPath As String
NewPath As String
TempPath As String
OriginalSha256 As String
Stage As Integer
End Type
Private CaseEntries() As CaseRenameEntry
Private CaseEntryCount As Long
Private CaseFolder As String
Private ProtectedInventory As Object
Private HashBaselineReady As Boolean
Private Function ExactLeafExists(ByVal folderPath As String, ByVal leafName As String) As Boolean
Dim fso As Object, item As Object
Set fso = CreateObject("Scripting.FileSystemObject")
For Each item In fso.GetFolder(folderPath).Files
If StrComp(item.Name, leafName, vbBinaryCompare) = 0 Then ExactLeafExists = True: Exit Function
Next item
End Function
Private Function FileSha256(ByVal filePath As String) As String
Dim fso As Object, stream As Object, hasher As Object
Dim fileBytes As Variant, digest As Variant, i As Long, result As String, failureText As String
On Error GoTo HashFailure
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FileExists(filePath) Then Err.Raise 53, , "hash対象fileがありません: " & filePath
Set stream = CreateObject("ADODB.Stream")
stream.Type = 1
stream.Open
stream.LoadFromFile filePath
stream.Position = 0
fileBytes = stream.Read
stream.Close
Set hasher = CreateObject("System.Security.Cryptography.SHA256Managed")
digest = hasher.ComputeHash_2(fileBytes)
For i = LBound(digest) To UBound(digest)
result = result & LCase$(Right$("0" & Hex$(CLng(digest(i)) And &HFF&), 2))
Next i
If Len(result) <> 64 Then Err.Raise 5, , "SHA-256が64桁ではありません: " & filePath
FileSha256 = result
Exit Function
HashFailure:
failureText = CStr(Err.Number) & " " & Err.Description
On Error Resume Next
If Not stream Is Nothing Then stream.Close
On Error GoTo 0
Err.Raise vbObjectError + 1840, "FileSha256", "SHA-256を読めないため停止します: " & filePath & ": " & failureText
End Function
Private Function FileMetadataFingerprint(ByVal filePath As String) As String
Dim fso As Object, item As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set item = fso.GetFile(filePath)
FileMetadataFingerprint = CStr(item.Size) & "|" & Format$(item.DateLastModified, "yyyymmddhhnnss")
End Function
Private Function HashMatches(ByVal filePath As String, ByVal expectedSha256 As String) As Boolean
HashMatches = (StrComp(FileSha256(filePath), expectedSha256, vbBinaryCompare) = 0)
End Function
Private Sub AssertExactPathAndHash(ByVal expectedPath As String, ByVal expectedSha256 As String, ByVal role As String)
Dim fso As Object, parentPath As String, leafName As String
Set fso = CreateObject("Scripting.FileSystemObject")
parentPath = fso.GetParentFolderName(expectedPath)
leafName = fso.GetFileName(expectedPath)
If Not ExactLeafExists(parentPath, leafName) Then Err.Raise 53, , role & "のpath/nameがcase exactではありません: " & expectedPath
If Not HashMatches(expectedPath, expectedSha256) Then Err.Raise 5, , role & "のSHA-256がbaselineと異なります: " & expectedPath
End Sub
Private Sub AssertProtectedInventory()
Dim key As Variant, fso As Object, protectedPath As String
If ProtectedInventory Is Nothing Then Err.Raise 5, , "protected SHA-256 inventoryがありません"
Set fso = CreateObject("Scripting.FileSystemObject")
For Each key In ProtectedInventory.Keys
protectedPath = fso.BuildPath(CaseFolder, CStr(key))
AssertExactPathAndHash protectedPath, CStr(ProtectedInventory(key)), "未対象file"
Next key
End Sub
Private Sub WriteFixtureAscii(ByVal filePath As String, ByVal payload As String)
Dim fso As Object, writer As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set writer = fso.CreateTextFile(filePath, True, False)
writer.Write payload
writer.Close
End Sub
Private Sub AssertSameSizeSameSecondMtimeChangeIsRejected()
Dim fso As Object, fixtureFolder As String, fixturePath As String
Dim beforeMetadata As String, afterMetadata As String, beforeHash As String, afterHash As String
Dim attempt As Long, fixturePassed As Boolean, failureText As String
On Error GoTo FixtureFailure
Set fso = CreateObject("Scripting.FileSystemObject")
Randomize
fixtureFolder = fso.BuildPath(fso.GetSpecialFolder(2), ".ittrip-seq184-hash-fixture-" & Format$(Now, "yyyymmddhhnnss") & "-" & CStr(Int(Rnd() * 1000000)))
If fso.FolderExists(fixtureFolder) Or fso.FileExists(fixtureFolder) Then Err.Raise 58, , "fixture pathが既にあります"
fso.CreateFolder fixtureFolder
fixturePath = fso.BuildPath(fixtureFolder, "same-metadata.bin")
For attempt = 1 To 20
WriteFixtureAscii fixturePath, "AAAA"
beforeMetadata = FileMetadataFingerprint(fixturePath)
beforeHash = FileSha256(fixturePath)
WriteFixtureAscii fixturePath, "BBBB"
afterMetadata = FileMetadataFingerprint(fixturePath)
afterHash = FileSha256(fixturePath)
If beforeMetadata = afterMetadata Then Exit For
Next attempt
If beforeMetadata <> afterMetadata Then Err.Raise 5, , "same-size/same-second-mtime fixtureを準備できません"
If StrComp(beforeHash, afterHash, vbBinaryCompare) = 0 Then Err.Raise 5, , "fixtureの異なる内容が同じSHA-256になりました"
If HashMatches(fixturePath, beforeHash) Then Err.Raise 5, , "hash gateがsame-size/same-second-mtime内容変更を誤って受理しました"
Debug.Print "same-size/same-second-mtime fixture rejected=True", beforeHash, afterHash
fixturePassed = True
FixtureCleanup:
On Error Resume Next
If Len(fixtureFolder) > 0 And fso.FolderExists(fixtureFolder) Then fso.DeleteFolder fixtureFolder, True
On Error GoTo 0
If fixturePassed Then Exit Sub
Err.Raise vbObjectError + 1841, "AssertSameSizeSameSecondMtimeChangeIsRejected", failureText
FixtureFailure:
failureText = CStr(Err.Number) & " " & Err.Description
Resume FixtureCleanup
End Sub
Windowsの一般的なcase-insensitive filesystemではcaseだけのName変更が直接反映されないことがあり、一意なtemporary名を経由する。UnicodeのLCase/UCaseはlocaleや文字で一対一でない場合もある。ファイル名の大小文字だけを一括変換では取得不能、対象なし、値が空という三状態を分け、stderrや終了コードを捨てません。
一時名を経由して一件変更
一時名を経由して一件変更ではfolder内mappingを作ってcollisionを全件検査し、Approvedの一件ごとにtemporary path経由でcaseを変更する。ファイル名の大小文字だけを一括変換のサンプルにあるパス、セル、ユーザー、時刻は検証用なので、直前に確認した承認値へ置き換えます。
Sub BuildApprovedCaseMap()
Dim ws As Worksheet, fso As Object, oldKeys As Object, targetKeys As Object
Dim lastRow As Long, r As Long, oldPath As String, newPath As String, oldLeaf As String
Dim newLeaf As String, item As Object, key As String, i As Long
Set ws = ThisWorkbook.Worksheets("CaseMap")
Set fso = CreateObject("Scripting.FileSystemObject")
HashBaselineReady = False
CaseEntryCount = 0
Set ProtectedInventory = Nothing
CaseFolder = fso.GetAbsolutePathName(Trim$(CStr(ws.Range("B1").Value2)))
If Not fso.FolderExists(CaseFolder) Then Err.Raise 76, , "対象folderがありません"
AssertSameSizeSameSecondMtimeChangeIsRejected
lastRow = ws.Cells(ws.Rows.Count, "B").End(xlUp).Row
If lastRow < 2 Then Err.Raise 5, , "mapping行がありません"
ReDim CaseEntries(1 To lastRow - 1)
Set oldKeys = CreateObject("Scripting.Dictionary"): oldKeys.CompareMode = vbTextCompare
Set targetKeys = CreateObject("Scripting.Dictionary"): targetKeys.CompareMode = vbTextCompare
For r = 2 To lastRow
If StrComp(Trim$(CStr(ws.Cells(r, "D").Value2)), "Approved", vbBinaryCompare) = 0 Then
oldPath = fso.GetAbsolutePathName(Trim$(CStr(ws.Cells(r, "B").Value2)))
newPath = fso.GetAbsolutePathName(Trim$(CStr(ws.Cells(r, "C").Value2)))
If StrComp(fso.GetParentFolderName(oldPath), CaseFolder, vbTextCompare) <> 0 Or StrComp(fso.GetParentFolderName(newPath), CaseFolder, vbTextCompare) <> 0 Then Err.Raise 5, , "同じ承認folder直下だけを変換します: row=" & r
If StrComp(oldPath, newPath, vbTextCompare) <> 0 Or StrComp(oldPath, newPath, vbBinaryCompare) = 0 Then Err.Raise 5, , "caseだけが異なるold/newを指定します: row=" & r
oldLeaf = fso.GetFileName(oldPath): newLeaf = fso.GetFileName(newPath)
If Not ExactLeafExists(CaseFolder, oldLeaf) Then Err.Raise 53, , "old名をcase exactで確認できません: " & oldLeaf
If oldKeys.Exists(oldPath) Then Err.Raise 5, , "old名が重複しています: " & oldPath
If targetKeys.Exists(newPath) Then Err.Raise 5, , "変換後名がcase-fold衝突しています: " & newPath
oldKeys.Add oldPath, True: targetKeys.Add newPath, True
CaseEntryCount = CaseEntryCount + 1
With CaseEntries(CaseEntryCount)
.SheetRow = r
.OldPath = oldPath
.NewPath = newPath
.OriginalSha256 = FileSha256(oldPath)
.Stage = 0
End With
End If
Next r
If CaseEntryCount = 0 Then Err.Raise 5, , "Approved mappingがありません"
For Each item In fso.GetFolder(CaseFolder).Files
key = fso.GetAbsolutePathName(item.Path)
If targetKeys.Exists(key) And Not oldKeys.Exists(key) Then Err.Raise 58, , "未対象fileと変換後名が衝突します: " & item.Name
Next item
Set ProtectedInventory = CreateObject("Scripting.Dictionary"): ProtectedInventory.CompareMode = vbBinaryCompare
For Each item In fso.GetFolder(CaseFolder).Files
key = fso.GetAbsolutePathName(item.Path)
If Not oldKeys.Exists(key) Then ProtectedInventory.Add item.Name, FileSha256(item.Path)
Next item
For i = 1 To CaseEntryCount
CaseEntries(i).TempPath = fso.BuildPath(CaseFolder, ".ittrip-case-stage-" & Format$(Now, "yyyymmddhhnnss") & "-" & CStr(i))
If fso.FileExists(CaseEntries(i).TempPath) Or targetKeys.Exists(CaseEntries(i).TempPath) Then Err.Raise 58, , "temporary名が衝突しています"
AssertExactPathAndHash CaseEntries(i).OldPath, CaseEntries(i).OriginalSha256, "Approved source"
Next i
AssertProtectedInventory
For i = 1 To CaseEntryCount
ws.Cells(CaseEntries(i).SheetRow, "E").Value2 = "Validated"
ws.Cells(CaseEntries(i).SheetRow, "F").NumberFormat = "@"
ws.Cells(CaseEntries(i).SheetRow, "F").Value2 = CaseEntries(i).OriginalSha256
Debug.Print CaseEntries(i).OldPath, CaseEntries(i).NewPath, CaseEntries(i).TempPath, CaseEntries(i).OriginalSha256
Next i
HashBaselineReady = True
End Sub
case-fold後に同名となる複数file、Unicode不定、open fileがあればbatchを止める。ファイル名の大小文字だけを一括変換で変更が発生する場合は、新規出力、no-clobber、WhatIf、下書き表示など利用可能な安全機構を先に使います。
Approved mapだけ反映
Approved mapだけ反映では入力と出力を別々に再取得します。ファイル名の大小文字だけを一括変換の合格は、全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変であることです。件数だけでなく識別値と内容も照合します。
Sub RenameAllApprovedCaseMappings()
Dim ws As Worksheet, fso As Object, i As Long, primaryError As String, rollbackErrors As String
Set ws = ThisWorkbook.Worksheets("CaseMap")
Set fso = CreateObject("Scripting.FileSystemObject")
If Not HashBaselineReady Or CaseEntryCount = 0 Then Err.Raise 5, , "先にBuildApprovedCaseMapで全SHA-256 baselineを確定してください"
AssertProtectedInventory
For i = 1 To CaseEntryCount
If CStr(ws.Cells(CaseEntries(i).SheetRow, "D").Value2) <> "Approved" Then Err.Raise 5, , "承認状態が変わりました"
If StrComp(CStr(ws.Cells(CaseEntries(i).SheetRow, "F").Value2), CaseEntries(i).OriginalSha256, vbBinaryCompare) <> 0 Then Err.Raise 5, , "保存SHA-256が変わりました"
AssertExactPathAndHash CaseEntries(i).OldPath, CaseEntries(i).OriginalSha256, "実行直前Approved source"
If fso.FileExists(CaseEntries(i).TempPath) Then Err.Raise 58, , "temporary名が実行直前に衝突しました"
Next i
On Error GoTo Rollback
For i = 1 To CaseEntryCount
AssertExactPathAndHash CaseEntries(i).OldPath, CaseEntries(i).OriginalSha256, "phase1直前Approved source"
Name CaseEntries(i).OldPath As CaseEntries(i).TempPath
CaseEntries(i).Stage = 1
Next i
For i = 1 To CaseEntryCount
AssertExactPathAndHash CaseEntries(i).TempPath, CaseEntries(i).OriginalSha256, "phase2直前temporary"
Name CaseEntries(i).TempPath As CaseEntries(i).NewPath
CaseEntries(i).Stage = 2
Next i
For i = 1 To CaseEntryCount
AssertExactPathAndHash CaseEntries(i).NewPath, CaseEntries(i).OriginalSha256, "二相rename後Approved target"
Next i
AssertProtectedInventory
For i = 1 To CaseEntryCount
ws.Cells(CaseEntries(i).SheetRow, "E").Value2 = "RenamedAndHashVerified"
Next i
Exit Sub
Rollback:
primaryError = CStr(Err.Number) & " " & Err.Description
On Error Resume Next
For i = CaseEntryCount To 1 Step -1
Err.Clear
If CaseEntries(i).Stage = 2 Then
AssertExactPathAndHash CaseEntries(i).NewPath, CaseEntries(i).OriginalSha256, "rollback直前new"
If Err.Number = 0 Then Name CaseEntries(i).NewPath As CaseEntries(i).TempPath
End If
If Err.Number = 0 And (CaseEntries(i).Stage = 1 Or CaseEntries(i).Stage = 2) Then
AssertExactPathAndHash CaseEntries(i).TempPath, CaseEntries(i).OriginalSha256, "rollback直前temporary"
If Err.Number = 0 Then Name CaseEntries(i).TempPath As CaseEntries(i).OldPath
End If
If Err.Number <> 0 Then rollbackErrors = rollbackErrors & " row=" & CaseEntries(i).SheetRow & " move=" & Err.Description
Next i
For i = 1 To CaseEntryCount
Err.Clear
AssertExactPathAndHash CaseEntries(i).OldPath, CaseEntries(i).OriginalSha256, "rollback後Approved source"
If Err.Number <> 0 Then
rollbackErrors = rollbackErrors & " row=" & CaseEntries(i).SheetRow & " verify=" & Err.Description
ws.Cells(CaseEntries(i).SheetRow, "E").Value2 = "RollbackHashFailed"
Else
CaseEntries(i).Stage = 0
ws.Cells(CaseEntries(i).SheetRow, "E").Value2 = "RolledBackAndHashVerified"
End If
Next i
Err.Clear
AssertProtectedInventory
If Err.Number <> 0 Then rollbackErrors = rollbackErrors & " protected=" & Err.Description
On Error GoTo 0
HashBaselineReady = False
If Len(rollbackErrors) > 0 Then Err.Raise vbObjectError + 184, , "rename失敗=" & primaryError & "; rollback/hash検証失敗=" & rollbackErrors
Err.Raise vbObjectError + 185, , "rename失敗。全対象のold path/name/SHA-256と未対象SHA-256をrollback確認済み: " & primaryError
End Sub
oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない。ファイル名の大小文字だけを一括変換の結果が期待と違えば追加変更を重ねず、入力、対象範囲、locale・時刻、権限、製品仕様の順に戻って調べます。
caseだけで同一扱いになる場合
case-fold後に同名となる複数file、Unicode不定、open fileがあればbatchを止める。caseだけで同一扱いになる場合に当てはまるときは中止理由、対象識別子、終了コードまたはErr.Number、直前に成功した段階を保存します。
oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない。ファイル名の大小文字だけを一括変換の再試行は原因を直し、同じ入力と対象を再確認してから行います。警告抑止や強制上書きで通しません。
reverse mapで元表記へ戻す
old/new reverse mapでcollisionを再確認し、同じtemporary経由で元caseへ戻す。復元にも「old path、desired case path、temporary path、binary comparison、approval statusをmapにする」を用い、類似名の別対象へ処理しません。
- ファイル名の大小文字だけを一括変換の開始前状態
- 採用対象: old path、desired case path、temporary path、binary comparison、approval statusをmapにする
- 復元後の確認: 全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である
- 復元を止める条件: case-fold後に同名となる複数file、Unicode不定、open fileがあればbatchを止める
Unicode case foldの限界
全件成功するまでstatusを個別記録し、途中失敗時に残りへ続行するか承認を分ける。ファイル名の大小文字だけを一括変換を反復するときは、正常、差分なし、対象なし、要承認、失敗を別の状態として記録します。
| Unicode case foldの限界の主キー | old path、desired case path、temporary path、binary comparison、approval statusをmapにする |
| 採用条件 | 全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である |
| 空結果 | oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない |
| 中止条件 | case-fold後に同名となる複数file、Unicode不定、open fileがあればbatchを止める |
case-insensitive filesystemを考慮から証跡化する変換後名のcollisionを事前検査
ファイル名の大小文字だけを一括変換の証跡は「old path、desired case path、temporary path、binary comparison、approval statusをmapにする」を主キーにします。case-insensitive filesystemを考慮で確認した値と、実行直前・実行直後の値を同じ作業番号に保存し、表示名が似ている別対象や前回の結果を混ぜません。
folder内のold→new mapをpreviewで空結果を判定する一時名を経由して一件変更
ファイル名の大小文字だけを一括変換の空結果は「oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない」として扱います。folder内のold→new mapをpreviewで入力自体が存在するか、権限で見えていないか、条件に一致しないだけかを分け、0件という表示だけで成功・失敗を決めません。
Unicode case foldの限界から復旧可否を測るcase-insensitive filesystemを考慮
ファイル名の大小文字だけを一括変換の復旧判断では「old/new reverse mapでcollisionを再確認し、同じtemporary経由で元caseへ戻す」を採用します。Unicode case foldの限界を再確認し、復旧後に「全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である」へ戻ったかを別の読み取り処理で測定します。
ファイル名の大小文字だけを一括変換の事前確認では、old path、desired case path、temporary path、binary comparison、approval statusをmapにするを画面表示や標準出力だけで済ませず、実行日時と一緒に作業記録へ写します。Windowsの一般的なcase-insensitive filesystemではcaseだけのName変更が直接反映されないことがあり、一意なtemporary名を経由する。UnicodeのLCase/UCaseはlocaleや文字で一対一でない場合もあるという仕様があるため、似た名前の別対象、前回実行時の値、キャッシュされた表示を今回の対象と取り違えないことが重要です。
ファイル名の大小文字だけを一括変換のコードを実行した直後は、まず終了状態を保存し、その後に別の読み取り処理で「全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である」を確認します。ファイル名の大小文字だけを一括変換では同じコードの表示だけを合否判定に使うと部分成功や遅延反映を見逃すため、識別値、件数、内容の三点を照合します。
ファイル名の大小文字だけを一括変換で結果が得られない場合は、oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない。ファイル名の大小文字だけを一括変換ではこの状態と、権限拒否、入力形式の不一致、接続先や時刻の違いを一緒にしません。ファイル名の大小文字だけを一括変換の対象候補数、除外された候補、最後に成功した確認処理を残すと、再試行で同じ失敗を重ねずに済みます。
ファイル名の大小文字だけを一括変換を元へ戻す必要があるときは、old/new reverse mapでcollisionを再確認し、同じtemporary経由で元caseへ戻す。ファイル名の大小文字だけを一括変換の復元前にも変更後の識別値を再取得し、別担当者の更新が入っていないか確認します。ファイル名の大小文字だけを一括変換の復元結果も通常処理と同じ完了条件で測り、戻したつもりという報告だけで閉じません。
ファイル名の大小文字だけを一括変換を引き継ぐ記録には、全件成功するまでstatusを個別記録し、途中失敗時に残りへ続行するか承認を分ける。特に「case-fold後に同名となる複数file、Unicode不定、open fileがあればbatchを止める」に該当した場合は、実行を止めたこと自体を正しい結果として扱います。ファイル名の大小文字だけを一括変換の次回担当者が承認範囲と未処理対象を区別できるよう、作業番号と対象識別子を対応付けます。
ファイル名の大小文字だけを一括変換の作業記録には、開始前の対象候補、採用した識別値、実行したコード、終了後の実測、除外理由を同じ作業番号で保存します。「old path、desired case path、temporary path、binary comparison、approval statusをmapにする」を省くと別対象との比較になり得るため、日時、実行場所、製品版と一緒に残します。
Excel VBAを利用したファイル名の大文字・小文字一括変換方法を定期運用へ組み込む場合も初回は対話的に確認します。正常は「全approved fileがdesired caseで存在し、内容hashと拡張子、未承認file名が不変である」、空結果は「oldとnewがbinaryでも同一なら変更不要とし、temporary fileを作らない」、停止は「case-fold後に同名となる複数file、Unicode不定、open fileがあればbatchを止める」として報告し、次の担当者が同じ条件で追試できるようにします。
caseだけの変更は一時名を経由する
WindowsのFileExists/Dirは通常case-insensitiveなので、新旧がcaseだけ違うと新名の存在確認が元ファイル自身に一致します。同じ親フォルダかつvbTextCompareでは同一、vbBinaryCompareでは相違する組合せだけを受け付けます。
一意な一時名へ移してから希望caseへ移し、二段目が失敗した場合は一時名から旧名へ戻します。別フォルダへの移動や文字case以外の変更はこの手順の対象外です。
公式情報・参考資料
ファイル名の大小文字だけを一括変換のコマンド、API、対応範囲は次の公式一次資料で確認しました。確認日は2026年7月17日です。ファイル名の大小文字だけを一括変換の実行環境にあるman、–help、VBA Object Browser、Get-Helpも併用してください。

コメント