古い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
などを整理したい。