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?

# PowerShellで大量のUnityプロジェクトを検索・重複整理・容量チェックしてみた

0
Posted at

古いPCから新しいPCへ移行するため、過去に作ったUnityプロジェクトをGitHubへ退避することにした。

ただ、PCの中を確認すると、Unityプロジェクトがいろいろなフォルダに散らばっていた。

さらに、

同じ作品の古いバージョン
コピーしたプロジェクト
テスト用プロジェクト
容量の大きいプロジェクト

なども混ざっている。

1件ずつエクスプローラーで確認するのは大変だったので、PowerShellを使って、

Unityプロジェクトを探す
↓
必要な情報を取得する
↓
容量を確認する
↓
重複を整理する

ところまで自動化してみた。

今回は、そのときに使ったPowerShellのコマンドを整理する。


まず検索する場所を決める

今回は、Unityプロジェクトがありそうな場所を配列にした。

$roots = @(
    "$env:USERPROFILE\Documents\GitHub",
    "$env:USERPROFILE\Desktop"
)

このコードは、

Documents\GitHub
Desktop

の2か所を $roots に入れている。

@(...) は複数の値をまとめる配列。

このあと、

foreach ($root in $roots) {
    # 検索処理
}

とすることで、それぞれのフォルダを順番に検索できる。


Unityプロジェクトをどう判定するか

今回まず考えたのが、

「PCの中からどうやってUnityプロジェクトだけ見つけるか」

だった。

Unityプロジェクトには通常、

Assets
Packages
ProjectSettings

といったフォルダがある。

そこで今回は、

ProjectSettingsというフォルダを探す
↓
その親フォルダにAssetsもあるか確認する

という方法でUnityプロジェクトを判定した。


Get-ChildItem で ProjectSettings を探す

使ったのが Get-ChildItem。

Get-ChildItem $root `
    -Directory `
    -Recurse `
    -Filter ProjectSettings `
    -ErrorAction SilentlyContinue

1つずつ見る。

Get-ChildItem $root

$root の中にあるファイルやフォルダを取得する。

-Directory

ファイルではなく、フォルダだけを対象にする。

-Recurse

現在のフォルダだけではなく、その中のサブフォルダまで再帰的に探す。

たとえば、

Desktop
└─ old
   └─ Unity
      └─ SampleProject
         └─ ProjectSettings

のように深い場所にあっても検索できる。

-Filter ProjectSettings

名前が ProjectSettings のものだけを探す。

-ErrorAction SilentlyContinue

検索中にアクセスできないフォルダなどがあっても、エラー表示を抑えて処理を続ける。


Test-Path で Assets があるか確認する

ProjectSettings だけではなく、同じプロジェクト内に Assets があることも確認した。

Where-Object {
    Test-Path (Join-Path $_.Parent.FullName "Assets")
}

ここで、

Test-Path

は、

そのファイルやフォルダが存在するか

を確認するコマンド。

存在すれば True、なければ False が返る。


Join-Path はパスを組み立てる

この部分。

Join-Path $_.Parent.FullName "Assets"

は、

ProjectSettingsの親フォルダ
+
Assets

を組み合わせてパスを作っている。

たとえば、

C:\UnityProjects\SampleProject\ProjectSettings

を見つけた場合、

$_.Parent.FullName

は、

C:\UnityProjects\SampleProject

になる。

そこへ、

Assets

を追加して、

C:\UnityProjects\SampleProject\Assets

というパスを作る。

そのパスが本当に存在するかを Test-Path で確認している。


ここまでをつなげる

実際には、

Get-ChildItem $root -Directory -Recurse -Filter ProjectSettings -ErrorAction SilentlyContinue |
    Where-Object {
        Test-Path (Join-Path $_.Parent.FullName "Assets")
    }

のようにした。

処理の流れは、

ProjectSettingsを探す
↓
見つかったものを1件ずつ確認
↓
親フォルダにAssetsがあるものだけ残す

となる。

PowerShellのパイプ | を使うことで、検索結果をそのまま次の条件判定へ渡している。


プロジェクト名とパスを取得する

Unityプロジェクトだと判断できたら、その親フォルダから情報を取得する。

$projectPath = $_.Parent.FullName
$projectName = $_.Parent.Name
$projectPath

には、

C:\UnityProjects\SampleProject

のようなプロジェクトのフルパスが入る。

$projectName

には、

SampleProject

のようなフォルダ名が入る。


更新日時も持たせる

同じプロジェクトが複数残っていたので、

どれが一番新しいのか

も判断したかった。

そこで、Assets の中にあるファイルを更新日時順に並べて、一番新しいものを取得した。

$latest = Get-ChildItem (Join-Path $projectPath "Assets") `
    -Recurse `
    -File `
    -ErrorAction SilentlyContinue |
    Sort-Object LastWriteTime -Descending |
    Select-Object -First 1

ここでは、

Sort-Object LastWriteTime -Descending

で、更新日時の新しい順に並べている。

-Descending は降順。

そして、

Select-Object -First 1

で一番上、つまり最も新しい1件だけ取得する。

これによって、

$latest.LastWriteTime

から、そのプロジェクトで最近更新されたファイルの日時を取得できる。


情報を PSCustomObject にまとめる

プロジェクトごとに、

名前
更新日時
パス

をセットで持ちたかった。

そこで使ったのが PSCustomObject。

[PSCustomObject]@{
    Name    = $projectName
    Updated = $latest.LastWriteTime
    Path    = $projectPath
}

これで1つのプロジェクトを、

Name
Updated
Path

というプロパティを持つデータとして扱える。

たとえば後から、

$project.Name
$project.Updated
$project.Path

のように取得できる。

JavaでDTOのようなオブジェクトに情報をまとめる感覚に近かった。


Unityプロジェクトの容量も確認したかった

次に問題になったのが容量。

Unityプロジェクトには、

Library
Temp
Logs
obj

など、Unityが生成するファイルも多く含まれている。

そのため、プロジェクトフォルダ全体のサイズを見るとかなり大きくなる。

今回はGitHubへ保存するために必要な、

Assets
Packages
ProjectSettings

を対象に容量を計算した。


対象フォルダを順番に処理する

foreach ($folderName in @(
    "Assets",
    "Packages",
    "ProjectSettings"
)) {
    # 容量計算
}

このコードは、

Assets
↓
Packages
↓
ProjectSettings

を1つずつ $folderName に入れて処理する。


Measure-Object でファイルサイズを合計する

実際の容量計算では、

$sum = (
    Get-ChildItem $folder -Recurse -File -ErrorAction SilentlyContinue |
    Measure-Object Length -Sum
).Sum

とした。

まず、

Get-ChildItem $folder -Recurse -File

で、そのフォルダ以下のファイルをすべて取得する。

各ファイルには、

Length

というファイルサイズの情報がある。

それを、

Measure-Object Length -Sum

へ渡す。

これは、

各ファイルのLengthを全部足す

という処理。

最後の、

.Sum

で合計値だけを取り出す。


バイトをMBへ変換する

ファイルサイズの合計はバイトなので、そのままだと分かりづらい。

そこで、

[math]::Round($size / 1MB, 1)

とした。

これは、

バイト
↓
MBへ変換
↓
小数第1位までに丸める

という処理。

その値を、

SizeMB = [math]::Round($size / 1MB, 1)

として持たせた。

ここでの SizeMB はPowerShellに最初からある名前ではなく、今回自分で付けたプロパティ名。

つまり、

このUnityプロジェクトの容量をMB単位で持つ項目

として使っている。


150MB以下だけに絞る

容量を持たせると、

$projects |
    Where-Object {
        $_.SizeMB -le 150
    }

のような絞り込みができる。

この、

$_.SizeMB -le 150

は、

現在処理しているプロジェクトのSizeMBが
150以下か

を判定している。

-le は less than or equal で「以下」。

そのため、

Where-Object {
    $_.SizeMB -le 150
}

は、

150MB以下のプロジェクトだけ残す

という意味になる。


同じ作品が複数あった

過去作品を探していると、同じ作品が複数の場所に残っていた。

たとえば、

Documents\ProjectA
Desktop\backup\ProjectA
Desktop\old\ProjectA

のような状態。

全部GitHubへ保存すると重複してしまう。

そこで、同じ名前のプロジェクトをまとめて、その中から最新版だけ残すことにした。


Group-Object で同じ名前をまとめる

$projects |
    Group-Object Name

とすると、同じ Name を持つデータを1つのグループとしてまとめられる。

イメージとしては、

ProjectA
├─ 古いProjectA
├─ 少し新しいProjectA
└─ 最新ProjectA

ProjectB
├─ 古いProjectB
└─ 最新ProjectB

のようになる。


グループごとに最新版だけ取る

さらに、

$projects |
    Group-Object Name |
    ForEach-Object {
        $_.Group |
            Sort-Object Updated -Descending |
            Select-Object -First 1
    }

とした。

1つずつ見る。

$_.Group

現在処理している同名プロジェクトのグループ。

Sort-Object Updated -Descending

更新日時の新しい順に並べる。

Select-Object -First 1

一番新しい1件だけ取得する。

全体では、

同じ名前をまとめる
↓
更新日時の新しい順にする
↓
最新版だけ残す

という処理になる。


最後に表で確認する

整理した結果を確認するときは、

$projects |
    Sort-Object Updated -Descending |
    Format-Table Name, SizeMB, Updated, Path -AutoSize

を使った。

Sort-Object Updated -Descending

更新日時の新しい順にする。

Format-Table Name, SizeMB, Updated, Path

必要な情報を表として表示する。

-AutoSize

列の幅を自動調整する。

これで、

Name
SizeMB
Updated
Path

を一覧で確認できるようになった。


手で探す作業が「条件」に変わった

最初はエクスプローラーを開いて、

これはUnityプロジェクト?
これは新しい?
これはコピー?
これは容量が大きい?

と1件ずつ確認しようとしていた。

PowerShellで整理してみると、それぞれを条件として表現できた。

Unityプロジェクトか
↓
ProjectSettingsとAssetsがあるか

容量は大きすぎないか
↓
SizeMBで判定

同じ作品が複数ないか
↓
Group-Object

どれが最新版か
↓
UpdatedでSort-Object

人が目で判断していた作業を、

どういう条件なら機械的に判断できるか

に変えていく感覚だった。


まとめ

今回よく使ったPowerShellのコマンドは、

Get-ChildItem
ファイルやフォルダを取得する

Test-Path
パスが存在するか確認する

Join-Path
パスを組み立てる

Where-Object
条件に合うものだけ残す

Sort-Object
並べ替える

Select-Object
必要なデータだけ取り出す

Measure-Object
値を集計する

Group-Object
同じ値を持つデータをまとめる

Format-Table
結果を表として表示する

だった。

特に今回、

検索
↓
条件で絞る
↓
情報を付ける
↓
グループ化する
↓
最新版を選ぶ

という処理をPowerShellで組めたことで、

「大量のファイルを操作するためのコマンド」

というだけではなく、

PC内のデータを条件に沿って整理する

ためにも使えることが分かった。

次は、整理したUnityプロジェクトをGitで管理し、GitHub CLIからPrivateリポジトリを作成してpushしたときに使った、

git init
git add
git commit
git push

gh repo create
gh repo view
gh repo list

などを整理したい。

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?