PowerShellを使って特定のフォルダ内のファイルをZIPで圧縮する方法を安全に進める結論は「Get-ChildItemで対象一覧と容量を確認し、Compress-Archiveで新規ZIPへ出力、展開testと件数照合をしてから採用します。folder自体を含めるか中身だけを含めるかは-Pathの指定で変わるため先に決めます。」です。Windows上のPowerShellで、圧縮対象とZIP出力先を別directoryにし、既存archiveを上書きしないという前提を外すと同じ入力でも結果が変わります。最初にsource inventoryをread-onlyで固定し、archive作成・展開検証・復元を別工程にします。
圧縮前にsource件数と既存ZIPを確認する
ZIPを作成する前に、圧縮元のルート、含める相対パス、除外対象、ファイル件数と合計サイズを記録します。開いているファイルや既存ZIPへの上書きを避け、別名でCompress-Archiveを実行します。作成後はエントリー一覧を読み、検証用ディレクトリへ展開して元データとの件数とハッシュを照合します。
- Get-ItemとGet-ChildItemで対象path、件数、総容量を確認する
- DestinationPathがsource treeの外で未使用名か確認する
- free space、長いpath、開いているfileを確認する
- 機密file、hidden item、symlinkをarchiveへ含めてよいかreviewする
LiteralPathとwildcardでarchive rootが変わる
folder pathを渡す場合とfolder内の*を渡す場合でroot entryが変わります。archive作成成功は全fileの読取成功や復元可能性を保証しません。圧縮率0に近いmediaもあり、LengthとCompressedLengthを区別します。hidden fileの扱いはversionとplatformで差があるため、必須hidden itemはentry一覧で確認します。
新規作成・Update・Forceの影響を分ける
対象folderと既存ZIPを確認
$source='C:\Data\Report'
$zip='C:\Archives\Report-20260717.zip'
Get-ChildItem -LiteralPath $source -File -Recurse | Measure-Object Length -Sum
Test-Path -LiteralPath $zip
既存ZIPがTrueなら別名へ変えます。source内にDestinationを置くと自己包含や次回対象混入を招くので分離します。
folder単位で新規archiveを作る
Compress-Archive -LiteralPath 'C:\Data\Report' -DestinationPath 'C:\Archives\Report-20260717.zip' -CompressionLevel Optimal
LiteralPathで対象folder自体をarchiveへ含めます。既存ZIPへ-Forceで上書きする前に世代管理を選びます。
拡張子を限定して圧縮
Get-ChildItem -LiteralPath 'C:\Data\Report' -File -Filter '*.txt' -Recurse |
Compress-Archive -DestinationPath 'C:\Archives\Report-text-20260717.zip'
pipeline対象を先に画面と件数でreviewします。Filter条件で必要fileが欠けないかsampleを確認します。
隔離directoryへ展開test
$test=Join-Path $env:TEMP 'zip-verify-20260717'
New-Item -ItemType Directory -Path $test | Out-Null
Expand-Archive -LiteralPath 'C:\Archives\Report-20260717.zip' -DestinationPath $test
既存業務folderへ展開せず新規test directoryを使います。展開後に件数とhashを照合します。
ZIP entryを読み取り確認
Add-Type -AssemblyName System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::OpenRead('C:\Archives\Report-20260717.zip').Entries |
Select-Object FullName,Length,CompressedLength
entry名に絶対pathや想定外directoryがないか確認します。OpenReadで得たarchive objectは実装ではDisposeするようtry/finallyを使います。
隔離directoryへ展開して内容を試す
- source内へZIPを作る
- -Forceで正常世代を上書きする
- folder自体と中身の違いを見ない
- 展開testなしでbackup扱いする
- ZIPを暗号化済みと誤解する
ZipFileでentry名とsizeを直接読む
圧縮自体はsourceを変更しませんが、-Forceによる既存ZIP上書き、source内への出力、機密dataの持ち出しが危険です。新規timestamp名へ作成し、access権と暗号化要件を確認します。Compress-Archiveはpassword暗号化機能ではありません。失敗時は未完成ZIPだけを削除し、sourceを残します。採用後も元data削除は別の保管承認と復元test後に行います。
hidden fileと大容量fileの制約を確認する
entry件数、相対path、総Lengthをsource一覧と比較し、重要fileは展開後Get-FileHashで照合します。空folder、hidden item、long path、locked fileの扱いをsampleで確認します。test展開物は確認後、元dataと取り違えないよう規程に沿って片付けます。
sourceとZIP entryの対応で完成を判定する
Compress-Archiveは新規ZIPを別pathへ作り、entry一覧と隔離directoryへのExpand-Archiveで検証します。圧縮成功だけでなく、source件数とentry件数、相対path、hashを比較し、hidden itemが対象外になる現行挙動を明示します。
Report folderを新規ZIPへ保存する
$src=Get-ChildItem -LiteralPath 'C:\Data\Report' -File -Recurse -Force
$visible=@($src | Where-Object { -not ($_.Attributes -band [IO.FileAttributes]::Hidden) })
Compress-Archive -LiteralPath 'C:\Data\Report' -DestinationPath '.\Report.zip' -ErrorAction Stop
[pscustomobject]@{AllFiles=$src.Count;ExpectedVisible=$visible.Count;ZipBytes=(Get-Item '.\Report.zip').Length}
ZIP entryと圧縮失敗を展開testで判断する
- entryを検収:ZIPが作成されentry名・件数・展開後hashが期待sourceと一致する
- 圧縮対象なし:対象file 0件は空archiveを当然視せず、filter/path誤りとして作成前に停止する
- archive作成・展開error:既存destination、locked file、2GB制限、path長、disk不足は例外としてpartial ZIPを不採用にする
Compress-Archiveはhidden file/folderを無視するため、完全backup用途では欠落になります。-Updateは削除済みentryの扱いを含め同期mirrorではなく、同名ZIPを-Forceで上書きする前に旧archiveを保全します。
hidden・空folder・同名ZIPを境界にする
Add-Type -AssemblyName System.IO.Compression.FileSystem
$z=[IO.Compression.ZipFile]::OpenRead((Resolve-Path '.\Report.zip')); try { $z.Entries | Select-Object FullName,Length } finally { $z.Dispose() }
hidden、空directory、Unicode名、locked、巨大file、同名destinationをtestします。source/entry/展開件数、relative path、hash、例外、temporary展開先の削除結果を記録します。
ZIPが存在するだけではbackup成功になりません。entry一覧を読み、隔離先で展開testを行い、Compress-Archiveが扱わないhidden fileをsource件数から意識的に分けます。

コメント