「ただのコピー」で終わらせるな。VBScriptにおけるファイルコピーの「事前防衛ロジック」を極める
業務自動化の世界において、最も初歩的でありながら、最も多くのエンジニアが「現場の混乱」を招くミスを犯す。それが「容量を考慮しないファイル転送」だ。
数GB、あるいは数TBに及ぶデータのバックアップや、大量のログファイルを共有サーバへ転送するスクリプトを書いていないだろうか? もし、転送先のディスク容量が不足していた場合、あなたのスクリプトはどう動く?
「途中でエラーが出て止まる」?
甘い。
エラーが発生した瞬間、転送先には「中途半端に書き込まれた壊れたファイル」が残り、システムは不整合を起こし、復旧のためにエンジニアが深夜に呼び出されることになる。これは「自動化」ではなく「障害の自動生成」だ。
今日は、プロフェッショナルとして、ファイルコピーを実行する前に「転送先の空き容量(AvailableSpace)を検証し、リスクを事前に排除するガードロジック」の実装術を伝授する。
1. なぜ「単純な比較」では不十分なのか
初心者はこう書く。
`If FileSize < Drive.FreeSpace Then CopyFile`
これでは実務では通用しない。アーキテクトの視点から見れば、ここには2つの致命的な欠陥がある。
欠陥①:レースコンディション(競合状態)への無防備
ファイルシステムは動的な生き物だ。あなたのスクリプトが「空き容量はある」と判定した直後、別のプロセス(Windows Updateやログ出力など)がディスクを消費する可能性がある。判定と実行の間にわずかな「隙」がある限り、容量不足の確率はゼロにはならない。
欠陥②:安全マージン(Buffer)の欠如
「必要なサイズ」と「空き容量」がぴったり同じ場合、転送中にファイルシステムのメタデータ更新や、一時的なキャッシュ領域の消費で、確実にエラーが発生する。
設計指針:
「空き容量 > 必要ファイルサイズ + 安全マージン」という評価式を、堅牢な例外処理と共に実装しなければならない。
2. プロダクション品質のガードロジック実装
以下に示すのは、単なる判定関数ではない。エラーハンドリング、単位変換、そして「なぜ失敗したのか」を呼び出し元に伝えるための設計思想を盛り込んだコードだ。
'**
' Module: FileTransferGuard
' Description: ファイルコピー実行前のディスク容量チェックおよび安全な転送
' Author: Chief Architect
'**
Option Explicit
' 定数定義:実務における「安全マージン」の設計
' ここでは、予期せぬ書き込みに備え、常に 500MB のバッファを確保する設計とする
Const SAFETY_MARGIN_BYTES = 524288000 ' 500 * 1024 * 1024
Dim FSO
Set FSO = CreateObject("Scripting.FileSystemObject")
' --- メイン処理例 ---
Dim sourceFile, destFolder
sourceFile = "C:\Data\LargeBackup.zip"
destFolder = "D:\BackupTarget\"
' 実行判定
If CheckSpaceAndCopy(sourceFile, destFolder) Then
WScript.Echo "SUCCESS: ファイル転送が正常に完了しました。"
Else
WScript.Echo "FAILURE: ファイル転送に失敗しました。詳細はログを確認してください。"
WScript.Quit 1
End If
'**
' Function: CheckSpaceAndCopy
' Purpose: 容量をチェックし、安全が確認できた場合のみコピーを実行する
' Returns: Boolean (成功ならTrue, 容量不足またはエラーならFalse)
'**
Function CheckSpaceAndCopy(strSource, strDestDir)
On Error Resume Next
Dim objFile, objDrive, lngRequiredSize, lngAvailableSpace
Dim strDestDriveRoot
' 1. ソースファイルの存在確認
If Not FSO.FileExists(strSource) Then
WScript.Echo "[Error] ソースファイルが存在しません: " & strSource
CheckSpaceAndCopy = False
Exit Function
End If
' 2. ファイルサイズと必要容量の計算
Set objFile = FSO.GetFile(strSource)
lngRequiredSize = objFile.Size
' 3. 転送先ドライブの特定
' GetDriveはパスを受け取ると、そのパスが属するドライブオブジェクトを返す
strDestDriveRoot = FSO.GetDrive(strDestDir).DriveLetter & "\"
Set objDrive = FSO.GetDrive(strDestDriveRoot)
If Err.Number <> 0 Then
WScript.Echo "[Error] 転送先ドライブの取得に失敗しました: " & Err.Description
CheckSpaceAndCopy = False
Exit Function
End If
' 4. 空き容量の取得 (AvailableSpaceを使用)
' FreeSpaceではなくAvailableSpaceを使うのがプロの作法。
' AvailableSpaceはユーザーが利用可能な実際の空き容量を返す。
lngAvailableSpace = objDrive.AvailableSpace
' 5. ガードロジック:容量判定 (必要サイズ + マージン < 空き容量)
If (lngRequiredSize + SAFETY_MARGIN_BYTES) > lngAvailableSpace Then
WScript.Echo "[Critical] 容量不足を検知しました。"
WScript.Echo " 必要容量 (推定): " & FormatBytes(lngRequiredSize + SAFETY_MARGIN_BYTES)
WScript.Echo " 現在の空き容量: " & FormatBytes(lngAvailableSpace)
CheckSpaceAndCopy = False
Exit Function
End If
' 6. コピー実行
' ここまで到達したということは、安全が確認されている
FSO.CopyFile strSource, strDestDir, True ' True = 上書き許可
If Err.Number <> 0 Then
WScript.Echo "[Error] コピー実行中に致命的なエラーが発生しました: " & Err.Description
CheckSpaceAndCopy = False
Else
CheckSpaceAndCopy = True
End If
On Error GoTo 0
End Function
'**
' Function: FormatBytes
' Purpose: バイト単位の数値を人間が読みやすい形式(GB/MB)に変換する
'**
Function FormatBytes(lngBytes)
Dim dblSize
dblSize = CDbl(lngBytes)
If dblSize >= 1073741824 Then ' GB
FormatBytes = Round(dblSize / 1073741824, 2) & " GB"
ElseIf dblSize >= 1048576 Then ' MB
FormatBytes = Round(dblSize / 1048576, 2) & " MB"
Else
FormatBytes = Round(dblSize / 1024, 2) & " KB"
End If
End Function
3. アーキテクトが教える、さらに踏み込んだ設計の勘所
上記のコードをベースに、実務レベルのツールへ昇華させるための「3つの極意」を伝えておく。
① AvailableSpace vs FreeSpace
VBScriptの `Drive` オブジェクトには `FreeSpace` と `AvailableSpace` が存在する。
- `FreeSpace`: ディスク全体の空き領域。
- `AvailableSpace`: 現在実行中のユーザーが利用可能な領域。(クォータ制限などが適用されている場合、こちらが正しい)
業務自動化においては、必ず `AvailableSpace` を使用せよ。
② データベースとの連携における「冪等性(べきとうせい)」
もしこのスクリプトを、転送完了後にDBへ「完了フラグ」を立てるようなワークフローの一部として組み込むなら、「失敗した時に中途半端なファイルが残っていないこと」を保証しなければならない。
理想的な設計は、以下のフローだ。
1. 容量チェック(Guard)
2. 一時ファイル名でコピー(例: `data.zip.tmp`)
3. コピー完了後、リネームして本来のファイル名にする
4. DBへステータスを書き込む
これにより、万が一の容量不足やネットワーク遮断が発生しても、不完全なファイルが「完成品」として誤認されるリスクをゼロにできる。
③ ログ出力の重要性
`WScript.Echo` はデバッグには良いが、運用フェーズではゴミだ。
実務用のツールを作るなら、判定結果(`lngAvailableSpace` や `lngRequiredSize` の実数値)を必ずテキストログ、あるいはイベントログへ記録せよ。後から「なぜあの時バックアップが走らなかったのか?」を調査する際、その数値こそが唯一の真実となる。
結論
「動けばいい」という考え方は、プログラミングには通用するが、エンジニアリングには通用しない。
容量チェックという一行のガードロジックを実装する。その背後にある「レースコンディションへの懸念」と「安全マージンの確保」という思考プロセスこそが、ジュニアとシニアを分かつ境界線である。
堅牢なコードを書け。システムに「予測不可能な挙動」を許すな。
