はじめに
PowerShellで定型業務を自動化する際、「Dropbox内のファイルにアクセスしたい」という場面は頻繁にあります。しかし、ユーザー名やインストール設定によって、PCごとにDropboxフォルダのパスは異なります。
スクリプト内にパスをハードコーディングするのは、メンテナンス性の悪さから避けたいところ。かといって、単純な方法ではMicrosoft Store版のDropboxや、変化する仕様に対応しきれません。
この記事では、試行錯誤の末にたどり着いた、どのような環境でもDropboxのパス(eg. C:Users\<username>\Dropbox
→%USERPROFILE%\Dropbox
or D:\Dropbox
etc.. )を確実に見つけ出す、堅牢なPowerShell関数 Find-DropboxPath
の設計思想と、その完成版コードを紹介します。
この記事で目指すもの
- 環境に依存しない: レジストリや設定ファイルなど、複数の情報源を探索し、あらゆる状況に対応する。
- 信頼性の高い順に探索: OSの公式な情報から順に試すことで、処理の安定性を高める。
- 自己完結した関数: どこにでも持ち運べる、ポータブルな関数として実装する。
完成したコード (Find-DropboxPath
関数)
まず、完成版の関数を以下に示します。
# -------------------------------------------------------------------
#
# Find-DropboxPath: 複数方法でDropboxのルートフォルダパスを探索する
#
# 理由: PC環境やDropboxのバージョンによってパスの保存場所が異なるため、
# 複数の信頼性の高い場所を順番に探索し、スクリプトの堅牢性を高める。
#
# -------------------------------------------------------------------
function Find-DropboxPath {
Write-Host "Dropboxのパスを3段階の優先順位で探索します..."
# 方法1: Windows Cloud API レジストリ (最優先)
# OSネイティブのクラウド連携機能で使われる公式な場所であり、最も信頼性が高い。
try {
$baseRegPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SyncRootManager'
$userSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
# SIDは正規表現で特別な意味を持つ文字を含む可能性があるため、安全に扱うためにエスケープする。
$escapedSid = [regex]::Escape($userSid)
$regexPattern = "^Dropbox!$($escapedSid)!dbid:.*"
# Get-ChildItemでレジストリキーを取得すると、`.Name`プロパティにはフルパスが格納されてしまう。
# キー自体の名前(例: 'Dropbox!S-1-5...')と比較するため、`.PSChildName`プロパティを使用する。
$dropboxKey = Get-ChildItem -Path $baseRegPath -ErrorAction SilentlyContinue | Where-Object {
$_.PSChildName -match $regexPattern
} | Select-Object -First 1
if ($dropboxKey) {
# 発見したキーのパスを基準に、その子キーである`UserSyncRoots`のパスを組み立てる。
$userSyncRootsKeyPath = Join-Path -Path $dropboxKey.PSPath -ChildPath "UserSyncRoots"
if (Test-Path $userSyncRootsKeyPath) {
# `UserSyncRoots`キーが持つプロパティの中から、名前が現在のユーザーSIDであるものの「値」を取得する。
# これが目的のDropboxフォルダパスとなる。
$pathValue = Get-ItemPropertyValue -Path $userSyncRootsKeyPath -Name $userSid -ErrorAction SilentlyContinue
if ($pathValue -and (Test-Path $pathValue -PathType Container)) {
Write-Host " [OK] 方法1 (Cloud APIレジストリ) からパスを発見しました。" -ForegroundColor Green
return $pathValue
}
}
}
Write-Host " [NG] 方法1 (Cloud APIレジストリ) からは見つかりませんでした。" -ForegroundColor Gray
} catch { Write-Host " [NG] 方法1 (Cloud APIレジストリ) の探索に失敗しました。" -ForegroundColor Gray }
# 方法2: Dropbox独自の構成ファイル (次善策)
# アプリ固有の設定ファイル。仕様変更のリスクはあるが、多くの環境で有効なためバックアップとして探索する。
try {
$dbxJsonPath = "$env:LOCALAPPDATA\Dropbox\info.json"
if (-not (Test-Path $dbxJsonPath)) { $dbxJsonPath = "$env:APPDATA\Dropbox\info.json" }
if (Test-Path $dbxJsonPath) {
$json = Get-Content $dbxJsonPath | ConvertFrom-Json
# Null合体演算子(??)を使い、personalアカウントがなければbusinessアカウントのパスを試す。
$jsonPath = $json.personal.path ?? $json.business.path
if ($jsonPath) {
Write-Host " [OK] 方法2 (JSONファイル) からパスを発見しました。" -ForegroundColor Green
return $jsonPath
}
}
Write-Host " [NG] 方法2 (JSONファイル) からは見つかりませんでした。" -ForegroundColor Gray
} catch { Write-Host " [NG] 方法2 (JSONファイル) の解析に失敗しました。" -ForegroundColor Gray }
# 方法3: アプリケーション登録レジストリ (フォールバック)
# Windowsにインストールされたアプリが自身の情報を登録する、従来からの標準的な場所。
try {
$regPath = (Get-ItemProperty -Path 'HKCU:\Software\Dropbox\Client' -Name 'install_path' -ErrorAction Stop).install_path
if ($regPath) {
Write-Host " [OK] 方法3 (アプリ登録レジストリ) からパスを発見しました。" -ForegroundColor Green
return $regPath
}
} catch { Write-Host " [NG] 方法3 (アプリ登録レジストリ) からは見つかりませんでした。" -ForegroundColor Gray }
return $null
}
設計思想 - なぜこの方法なのか?
この関数の核心は、複数の方法を階層的に試すという、フォールバックの考え方にあります。これは、目的地までのルートを複数知っている、優秀なカーナビのようなものです。
第1優先: Windows Cloud API レジストリ
- Why: これはWindows OS自身が、エクスプローラーのサイドバー表示などのためにクラウドストレージを認識・管理する「公式」な記録です。DropboxがOSと連携している以上、この情報が最も正確で、将来にわたって安定している可能性が極めて高いと判断しました。
第2優先: Dropbox独自の構成ファイル (info.json
)
- Why: アプリケーション固有の設定ファイルです。アプリの仕様変更で場所や形式が変わるリスクはありますが、多くの環境で依然として有効なため、バックアップのルートとして確保しています。
第3優先: アプリケーション登録レジストリ
- Why: OSにインストールされたアプリケーションが、自身のインストール場所などを登録する従来からの標準的な場所です。これも高い信頼性を持つため、第1の方法が何らかの理由で使えない場合の、強力な次善策となります。
- ⚠️注意点⚠️: この方法は、主に公式サイトからダウンロードしたインストーラー(.exe形式)でインストールされた場合に有効です。Microsoft Storeからインストールされたアプリの場合、このレジストリキーは作成されないため、この探索は失敗します。
おわりに
PowerShellのスクリプトは、一度作って終わりではありません。様々な環境で、長く安定して動作するためには、今回のように複数の可能性を考慮した堅牢な設計が不可欠です。
単純なパス取得から始まったスクリプトも、デバッグと試行錯誤を重ねることで、最終的にこの信頼性の高い関数へとたどり着きました。