バックアップを取っている状態と、そこから戻せる状態は同じではない。
コピー先が途中で止まっていた、対象から外れていたフォルダーがあった、戻したファイルが壊れていた、といった食い違いは、実際に戻すまで表に出ない。
ここでは、いま使っているバックアップが戻せるかどうかを、元のデータに触れずに確かめる手順を整理する。
改訂した復元・比較スクリプトは2026年10月2日にWindows PowerShell 5.1で、隔離した練習用フォルダーを使って実行した。
復元テストで確かめるのは3つ
戻せるかどうかは、次の3点に分かれる。
| 確かめること | 分からないと起きること | 見る方法 |
|---|---|---|
| 取れているか | 対象から外れたフォルダーが丸ごと無い | ファイル数を数えて比べる |
| 戻せるか | 戻す操作そのものが失敗する | 別の場所へ実際に戻す |
| 中身が同じか | 戻ったが開けない、途中で切れている | ハッシュで突き合わせる |
ファイル数だけを比べると、中身が壊れていても気づけない。
逆にハッシュだけを見ると、そもそも対象から外れていたフォルダーには気づけない。
元のデータを上書きしない場所へ戻す
復元テストで最も避けたいのは、テストそのものが元のデータを壊すことである。
改訂版のスクリプトでは、元とバックアップから独立した未作成のフォルダー名を指定する。スクリプトが復元先を新しく作る。
元の場所へ戻す操作は、テストではなく本番の復旧であり、試す目的で実行しない。
- 元とバックアップの外へ、未作成の復元先を決める
- 元のフォルダーと、戻した先の両方を残したまま比べる
- 比べ終わるまで、戻した先を消さない
手順1 戻す(robocopyの終了コードを見る)
次の内容をrestore-test.ps1としてUTF-8(BOM付き)で保存する。通常のローカルフォルダーを対象とし、未作成の復元先を指定する。
param([Parameter(Mandatory=$true)][string]$Original,
[Parameter(Mandatory=$true)][string]$Backup,
[Parameter(Mandatory=$true)][string]$Output)
$ErrorActionPreference = 'Stop'
function Get-Tree([string]$Root) {
$base = (Get-Item -LiteralPath $Root -Force -ErrorAction Stop).FullName.TrimEnd('\')
if (-not (Get-Item -LiteralPath $base -Force).PSIsContainer) { throw 'フォルダーを指定してください' }
$rows = New-Object 'System.Collections.Generic.List[string]'
$rows.Add('#ROOT')
function Visit([string]$Path) {
foreach ($item in Get-ChildItem -LiteralPath $Path -Force -ErrorAction Stop) {
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'リンク・ジャンクションは対象外です' }
$rel = $item.FullName.Substring($base.Length + 1)
if ($item.PSIsContainer) {
$rows.Add('D:' + $rel)
Visit $item.FullName
} else {
$hash = (Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
$rows.Add('F:' + $rel + ':' + $hash)
}
}
}
if ((Get-Item -LiteralPath $base -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'リンクは対象外です' }
Visit $base
$rows | Sort-Object
}
$a = (Get-Item -LiteralPath $Original -Force).FullName.TrimEnd('\')
$b = (Get-Item -LiteralPath $Backup -Force).FullName.TrimEnd('\')
$dest = [IO.Path]::GetFullPath($Output).TrimEnd('\')
if (Test-Path -LiteralPath $dest) { throw '復元先は未作成の場所にしてください' }
foreach ($src in @($a, $b)) {
if ($dest -eq $src -or $dest.StartsWith($src + '\', [StringComparison]::OrdinalIgnoreCase) -or $src.StartsWith($dest + '\', [StringComparison]::OrdinalIgnoreCase)) { throw '復元先と入力の包含関係は禁止です' }
}
$before = @(Get-Tree $a)
$backupBefore = @(Get-Tree $b)
New-Item -ItemType Directory -Path $dest -ErrorAction Stop | Out-Null
& robocopy.exe $b $dest /E /XJ /R:1 /W:1 /NFL /NDL /NJH /NP | Out-Host
$copyCode = $LASTEXITCODE
Write-Host "RobocopyExit=$copyCode"
if ($copyCode -ge 8) { throw 'コピー失敗。出力は未完成です' }
$restored = @(Get-Tree $dest)
$difference = @(Compare-Object $before $restored)
if (@(Compare-Object $before @(Get-Tree $a)).Count -or @(Compare-Object $backupBefore @(Get-Tree $b)).Count) { throw '検証中に入力が変わりました' }
$difference | Select-Object SideIndicator,InputObject | ConvertTo-Json
if ($difference.Count) { throw '相対パス・ファイル内容・空フォルダーに差があります' }
Write-Host '対象の通常ファイルとフォルダーが一致しました'
robocopyは終了コードで結果を返すため、コードを確認し、その後にファイルとフォルダーを照合する。
powershell.exe -NoProfile -ExecutionPolicy RemoteSigned -File ".\restore-test.ps1" -Original ".\original" -Backup ".\backup" -Output ".\restore-new"
| 終了コード | 意味 | 扱い |
|---|---|---|
| 0 | コピーする必要のあるファイルがなかった | 指定した入力と照合結果を確認する |
| 1 | ファイルをコピーした | 成功。次の手順へ |
| 2 | コピー先にだけ余分なものがある | 内容を確かめる |
| 3 | コピーした、かつ余分なものもある | 内容を確かめる |
| 8以上 | コピーできなかったものがある | 失敗として扱う |
手元で3ファイルをコピーしたときの終了コードは1だった。
0はコピーする必要のあるファイルがなかった場合にも返る。すでに同じ内容のコピー先でも0になるため、指定した入力と照合結果を確認する。
8以上が返ったときは、その時点で失敗として扱い、次の手順へ進まない。
手順2 中身が同じかをハッシュで突き合わせる
ファイル数が合っていても、中身が同じとは限らない。
元と戻した先の両方でハッシュを取り、突き合わせる。
function Get-Tree([string]$Root) {
$base = (Get-Item -LiteralPath $Root -Force -ErrorAction Stop).FullName.TrimEnd('\')
if (-not (Get-Item -LiteralPath $base -Force).PSIsContainer) { throw 'フォルダーを指定してください' }
$rows = New-Object 'System.Collections.Generic.List[string]'
$rows.Add('#ROOT')
function Visit([string]$Path) {
foreach ($item in Get-ChildItem -LiteralPath $Path -Force -ErrorAction Stop) {
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'リンク・ジャンクションは対象外です' }
$rel = $item.FullName.Substring($base.Length + 1)
if ($item.PSIsContainer) {
$rows.Add('D:' + $rel)
Visit $item.FullName
} else {
$hash = (Get-FileHash -LiteralPath $item.FullName -Algorithm SHA256 -ErrorAction Stop).Hash
$rows.Add('F:' + $rel + ':' + $hash)
}
}
}
if ((Get-Item -LiteralPath $base -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) { throw 'リンクは対象外です' }
Visit $base
$rows | Sort-Object
}
$a = @(Get-Tree ".\original")
$b = @(Get-Tree ".\restore-new")
Compare-Object $a $b | Select-Object SideIndicator,InputObject
パスの先頭を切り落としているのは、置き場所が違っても同じファイルとして比べるためである。
何も出力されなければ、比較した通常ファイルの相対パスとSHA-256、およびフォルダーの相対パスが一致している。ACL、代替データストリーム、暗号化属性やOS全体の復元を検証するものではない。
手元では3ファイルをコピーした直後に実行し、出力は空だった。
壊れていたときの出方
戻した側の1ファイルだけを書き換えてから、同じコマンドを実行した。
SideIndicator rel
------------- ---
=> \b.txt
<= \b.txt改訂版ではInputObjectにF:相対パス:ハッシュを表示する。同じ相対パスでもハッシュが違えば2行出て、矢印の向きが分かれる。
これは「両方に存在するが、ハッシュが違う」という意味になる。
| 出方 | 意味 |
|---|---|
| 同じrelが2行、矢印が両方向 | 両方にあるが中身が違う |
| 矢印が元の側だけ | 戻した先にそのファイルが無い |
| 矢印が戻した先だけ | 元に無いファイルが戻した先にある |
ファイル履歴やイメージを使っている場合
Windowsの標準機能を使っている場合も、確かめる形は変わらない。
戻す操作の入り口が違うだけで、別の場所へ戻してハッシュで比べる流れは同じである。
| 使っている仕組み | 戻す入り口 | テストのときの注意 |
|---|---|---|
| ファイル履歴 | コントロールパネルのファイル履歴から個人用ファイルを復元 | 復元先を元のフォルダーにしない |
| OneDriveなどの同期 | Webの版管理から取り出す | 同期フォルダーの外へ出して比べる |
| 自分でコピーしている | コピー先から取り出す | コピー元と同時に開いたまま比べる |
ファイル履歴の動作状況は、サービスの状態からも確かめられる。
Get-Service -Name fhsvc | Select-Object Name,DisplayName,Status,StartType手元の環境ではStatusがStopped、StartTypeがManualと表示された。
この表示は、機能が有効かどうかを直接示すものではないため、設定画面の側もあわせて見る。
点検を続ける形にする
一度戻せたことは、次も戻せることを保証しない。
対象フォルダーが増えたり、保存先の空き容量が尽きたりすると、そこから先が取れなくなる。
- 月に一度、同じ手順で1つのフォルダーだけ戻して比べる
- 確かめた日と、そのときのファイル数を書き留める
- 保存先の空き容量を同じタイミングで見る
- 対象フォルダーを増やしたら、その月のうちに1回試す
全部を戻すテストは時間がかかるため、続けられる大きさに絞るほうが結果的に回数を重ねられる。
練習で検出した差と復元の範囲
練習用PDF・PNG・検証スクリプトをダウンロード(ZIP)。
2026年10月2日に作った架空の文字と模様で、個人情報や実務の資料は含みません。
ZIPのrestore-test.ps1と記事内のスクリプトは同じ内容です。
originalとbackupの2つの練習用フォルダーに同じファイルを入れ、未作成のrestore-newへ戻します。
出力がすでに存在する場合や、入力の内側・外側に出力を重ねる指定ではコピー前に止まります。
| 2026年10月2日に実行した条件 | 結果 |
|---|---|
| 日本語と角括弧のファイル名、隠しファイル、0バイトファイル、空フォルダー | 復元して相対パス・バイト内容・フォルダーが一致 |
| 既存の復元先、入力内の復元先 | コピー前に拒否 |
| バックアップの1ファイルを変更/不足/追加 | 3ケースとも照合で不一致を検出 |
Get-Treeは絶対パスへ解決してから相対パスを作り、-LiteralPathと-Forceで角括弧や隠しファイルも扱います。
空フォルダーも比較し、リンクやジャンクションは対象外として止めます。
失敗後の新しい出力は未完成として保持し、原因を確認してから別の未作成の場所で再試行します。
robocopyの/R:1 /W:1は、失敗時の再試行を1回、間隔を1秒へ制限します。
標準設定は100万回・30秒のため、短い復元テストでは明示します。
2・3は余分な項目、5・6・7は不一致などを含むため、8未満でも照合を省略しません。
この確認は同じPC内の練習用ファイルをコピーして戻したものです。
外付けディスク、クラウドの版管理、Windowsのシステムイメージ、ファイル履歴の実際の復元操作は未検証です。
現在のバックアップに含めたいフォルダーがそろっているかは、元とバックアップの対象範囲を別に確認します。
Microsoftのrobocopy仕様を2026年10月2日に確認しました。
元に戻す際は検証用コピーを本番へ上書きせず、保持した元と、必要な版のバックアップを使います。
あなたはどっち?利用環境に合わせた最適プラン判定フロー
使っているバックアップの仕組みから順に当てはめると、復元テストの入り口が決まります。2026年10月2日にWindows PowerShell 5.1で、隔離した練習用フォルダーを使って確認した内容にもとづく目安です。
- まず、取れているか(ファイル数)、戻せるか(実際に別の場所へ戻す)、中身が同じか(ハッシュ)の3点が別のことだと理解する。
- 元のデータとバックアップの外に、まだ作っていない復元先のフォルダー名を決める。
- ファイル履歴なら、コントロールパネルから個人用ファイルを復元する。復元先は元のフォルダーにしない。
- OneDriveなどの同期なら、Webの版管理から取り出し、同期フォルダーの外へ出して比べる。
- 自分でコピーしているなら、コピー先から取り出し、コピー元と同時に開いたまま比べる。
- 月に一度、1つのフォルダーだけ戻して比べ、確かめた日とファイル数を書き留める。
元の場所へ戻す操作はテストではなく本番の復旧です。試す目的では実行しないでください。
まとめ
- 取れているか、戻せるか、中身が同じかは別のことなので、3つとも確かめる
- 改訂版の復元先は未作成の場所にし、スクリプトに作成させる
- robocopyは画面ではなく終了コードで判断する。1はコピー成功、8以上は失敗
- Get-FileHashとCompare-Objectで、相対パスとハッシュの組を突き合わせる
- 同じ相対パスが2行、矢印が両方向に出たら、中身が違っている
- 月に一度、小さい範囲でよいので続けて試す
コマンドレットの詳細はMicrosoft Learnのリファレンスで確認できる。
Windowsの作業を楽にする道具はPCメソッドのまとめにも置いている。
この改訂の練習データとコードは、記事内のZIPから取得できます。
よくある質問:バックアップの復元テストで迷ったときのチェックリスト
バックアップを取っていれば戻せますか?
同じではありません。コピー先が途中で止まっていた、対象から外れたフォルダーがあった、戻したファイルが壊れていたといった食い違いは、実際に戻すまで表に出ません。
ファイル数を比べるだけで十分ですか?
足りません。ファイル数だけでは、中身が壊れていても気づけません。逆にハッシュだけでは、対象から外れていたフォルダーに気づけません。
復元テストはどこへ戻せばよいですか?
元のデータとバックアップの外にある、まだ作っていないフォルダーへ戻します。元のフォルダーと、戻した先の両方を残したまま比べ、比べ終わるまで戻した先を消さないでください。
ファイル履歴を使っているときの注意は?
復元先を元のフォルダーにしないことです。fhsvcサービスの状態は参考になりますが、機能が有効かどうかを直接示すものではないため、設定画面も合わせて見ます。
一度戻せたら、ずっと安心ですか?
安心できません。対象フォルダーが増えたり、保存先の空き容量が尽きたりすると、そこから先が取れなくなります。月に一度、1つのフォルダーだけ同じ手順で試す形にします。
この記事のスクリプトは実機で確かめていますか?
2026年10月2日にWindows PowerShell 5.1で、隔離した練習用フォルダーを使って実行しました。実務のバックアップやファイル履歴の実機復元は、この確認に含まれていません。
