0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

フォルダ別無圧縮ファイル作成.ps1

Posted at

1. スクリプト概要

スクリプト名: フォルダ別無圧縮ファイル作成.ps1
対象フォルダ直下の子フォルダを無圧縮のZIPファイルとして個別に作成します

2. 処理と目的

処理の流れ
 1. 対象フォルダのフルパスを指定します
 2. 対象フォルダ内の子フォルダ一覧を取得します
 3. 手順2で取得した各フォルダのファイル数を取得します
 4. 手順3で取得した情報を含むフォルダ一覧をグリッド表示し、ユーザーに選択させます
 5. 選択されたフォルダ名で一時フォルダにZIPファイルを作成します(例: 選択されたフォルダ名.zip
 6. 作成されたZIPファイルを対象フォルダへ移動します
 7. 選択された元のフォルダを削除します
 8. 手順5~7を選択されたフォルダの数だけ繰り返します

目的
 画像ファイルなど圧縮率の悪いファイルが大量に存在するフォルダを扱う際、ファイルが個別に分かれていると処理に時間がかかることがあります。本スクリプトは、ファイル数を減らすことで処理時間を最適化することを目的としています。

3. 動作環境と要件

PowerShellのバージョン
7.0以上

OS
Windows10

必要なモジュール
System.IO.Compression.FileSystem (通常、PowerShellに標準で含まれています)

必要な権限
特になし

その他の設定
特になし

4. 使用方法

基本的な実行方法
スクリプトコードを拡張子ps1で保存してPowershellで実行してください。
ファイルを保存する際は、文字コードをUTF8 BOM付にしてください。

パラメータ
なし

使用例

  1. コマンドラインでpwsh フォルダ別無圧縮ファイル作成.ps1を実行
  2. 「圧縮対象フォルダ」の入力プロンプトが表示されるので、対象フォルダのフルパスを入力してください

5. スクリプトコード

if($PSVersionTable.PSVersion.Major -ge 7 -and ((ps -Id $PID | ?{$_.Parent.ProcessName -notin @('pwsh','powershell')}) -ne $null)){
    cls
}

# ウィンドウのタイトルをスクリプト名に設定します。
$host.UI.RawUI.WindowTitle = ([IO.Path]::GetFilenameWithoutExtension($PSCommandPath))

# エラー発生時にスクリプトの実行を停止するよう設定します。
$ErrorActionPreference = 'Stop'

# ZIPファイル操作に必要なアセンブリを読み込みます。
Add-Type -Assembly System.IO.Compression
Add-Type -Assembly System.IO.Compression.FileSystem

echo "--------------------------------------------------------------------------"
Write-Host "対象フォルダ直下の子フォルダ別に無圧縮でZIPファイルを作成します" -ForegroundColor White -BackGroundColor Black
Write-Host "圧縮する子フォルダは選択可能です" -ForegroundColor White -BackGroundColor Black
echo "--------------------------------------------------------------------------"

# 圧縮対象フォルダが入力されるまでループします。
do {
    $taishoDir = Read-Host "圧縮対象フォルダ"
    $taishoDir = $taishoDir.Trim('"')
# 入力されたパスが存在するディレクトリか確認します。存在しない場合は再度入力を促します。
} while( -not ([IO.Directory]::Exists($taishoDir)) )
$dstPath = $taishoDir

# 対象フォルダ直下のサブフォルダ一覧を取得します。
$dirlist = ls -LiteralPath $taishoDir -Directory

# 取得した各サブフォルダに対して追加情報を付与します。
foreach($var in $dirlist){
    # 子フォルダの状態(直下のサブフォルダ名)を追加します。
    $childDir = ls -literal $var.fullname -Directory | %{$_.Name}
    if($childDir -ne $null){
        $dirStr = [String]::Join(', ', $childDir)
    } else {
        $dirStr = $null
    }
    # 取得した子フォルダ名を'childDir'プロパティとして追加します。
    $var | Add-Member 'childDir' $dirStr

    # フォルダ内の拡張子別のファイル数をプロパティに追加します。例: ".txt=5, .log=2"
    $extensionGroup = ls -literal $var.fullname -File -Recurse | %{$_.Extension} | group
    $filesStr = ($extensionGroup | %{"$($_.Name)=$($_.Count)"}) -join ', '
    $var | Add-Member 'files' $filesStr
}

# サブフォルダが2件以上ある場合、選択画面を表示します。
if( ($dirlist|Measure).count -gt 1){
    # Out-GridView を使用して、ZIP化対象のフォルダをユーザーに選択させます。
    $selectdata = $dirlist | select Name,childDir,files,fullname, CreationTime,LastWriteTime | Out-GridView -Title "ZIP化対象のフォルダを選択してください" -PassThru
    $selectpathlist = $selectdata | %{$_.fullname}
    $dirlist = $dirlist | ?{$_.fullname -in $selectpathlist}
} else {
    # サブフォルダが1件以下の場合、選択画面は表示せず、そのままのリストを使用します。
    $dirlist = $dirlist
}


echo "`n`n`n`n"

echo "------------------------------------------------------"
$dirlist | %{Write-Host $_.Name -ForegroundColor Cyan}
echo "------------------------------------------------------"
echo "上記の$(($dirlist|Measure).Count)個のフォルダを無圧縮ZIPでアーカイブして元フォルダを削除します"
pause

# 初期化します。
$progressCount = 0
$allCount = ($dirlist|Measure).Count

# 各対象フォルダに対してZIPアーカイブと元のフォルダ削除処理を実行します。
foreach( $dirdata in $dirlist){
    
    $progressCount += 1
    Write-Host "(${progressCount}/${allCount}) $($dirdata.fullname) を圧縮&削除 開始..." -ForegroundColor White

    if( (ls -LiteralPath $dirdata.fullname) -eq $null){
        Write-Warning "フォルダ「$($dirdata.fullname)」内にファイルがありません!"
        continue
    }

    Set-Location -LiteralPath $dirdata.fullname

    # 一時的なZIPファイルのパスをランダムなファイル名で生成します。
    # 既存のファイル名と重複しないようにループで確認します。
    do {
        $tmppath = Join-Path ([IO.Directory]::GetParent($dirdata.fullname)).fullname (([IO.Path]::GetRandomFilename()) + ".zip")
    } while( (Test-Path -literal $tmppath) )

    # 最終的なZIPファイルのパスを「フォルダ名.zip」として生成します。
    $dstFullname = Join-Path ([IO.Directory]::GetParent($dirdata.fullname)).fullname "$($dirdata.Name).zip"

    Write-Host "src=$($dirdata.fullname)" -ForegroundColor Cyan
    Write-Host "dst=$($dstFullname)" -ForegroundColor Green

    # フォルダを無圧縮でZIPアーカイブします。
    [System.IO.Compression.ZipFile]::CreateFromDirectory($dirdata.fullname, $tmppath, ([System.IO.Compression.CompressionLevel]::NoCompression), $false)

    # 最終的なZIPファイル名が既に存在する場合、リネームして重複を回避します。
    while(Test-Path -literal $dstFullname){
        Write-Warning "名前「$(([IO.Path]::GetFilename($dstFullname)))」が重複していたためリネームします。"
        $dstFullname = $dstFullname.replace('.zip','(1).zip')
    }
    # 一時ファイル名を最終的なファイル名にリネームします。
    ren -literal $tmppath -NewName ([IO.Path]::GetFilename($dstFullname))

    # ZIPファイルの作成日時と更新日時を元フォルダの情報を基に設定します。
    # 作成日時: 元フォルダの作成日時
    # 更新日時: 元フォルダ内のファイルの中央値の更新日時(厳密にはソート後の中央のファイルの更新日時)
    $filelist = ls -literal $dirdata.fullname -Recurse -File | sort LastWriteTime
    $maxCreationTime  = $dirdata.CreationTime
    # ファイルリストが空の場合、エラーを避けるために条件を追加
    if ($filelist.Count -gt 0) {
        $maxLastWriteTime = $filelist[[int]($filelist.Count/2)].LastWriteTime
    } else {
        $maxLastWriteTime = $dirdata.LastWriteTime # ファイルがない場合はフォルダの更新日時を使用
    }

    # 作成したZIPファイルのプロパティを取得し、作成日時と更新日時を設定します。
    $archiveProperty = gi -literal $dstFullname
    $archiveProperty.CreationTime = $maxCreationTime
    $archiveProperty.LastWriteTime = $maxLastWriteTime

    Set-Location -LiteralPath $dirdata.PSParentPath
    rmdir -LiteralPath $dirdata.fullname -Recurse -Force

}

pause

6. 注意事項と既知の問題

制約事項
大量のファイルやサイズの大きいファイルを処理する場合、完了までに時間がかかる可能性があります。

既知のバグ
もしバグを発見された場合は、コメントでご報告ください。

トラブルシューティング
・ps1ファイルのエンコーディングには注意してください。
・PowerShell 7未満では動きません

7. 免責事項

本スクリプトにはいかなる保証もありません。使用は自己責任で行ってください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?