画像ファイルを名前の日付で分類(DateTime.TryParseExact の使い方メモ)

DateTime.TryParseExact メソッド (System)

が結構便利そうだったので使い方の確認がてらPowerShellでタイトルの処理を作ってみた。

対象のファイル群

Dropboxの自動アップロードによってスマホからPCに同期された画像ファイルに対して処理を行う。

画像ファイルの名前は自動で2018-03-23 12.34.56.jpgといった形式となるため、その名前を日付に変換して分類を行う。

コード

<#
.Synopsis
Dropboxでアップロードされた画像ファイルを日付で分類する
yyMMのフォルダを作成して、その中に移動する
#>

[string]$rootPath = 
    # カレントディレクトリ以下のファイルを対象にする場合
    $PWD.ProviderPath
    # PS1として保存して、その保存先フォルダ内を対象にする場合
    #[IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Definition)

# Dropboxで自動アップロードされたファイルは以下のような名前になる
# e.g. 2018年3月23日12時34分56秒に撮影した画像の場合
# 2018-03-23 12.34.56.jpg
[string]$dateFormat = 'yyyy-MM-dd HH.mm.ss'

# DateTime.TryParseExact の引数指定が面倒だったため、スクリプトブロックに格納
[scriptblock]$tryParse = {
    param([string]$dateString, [ref]$outDate)
    return [datetime]::TryParseExact(
            $dateString, 
            $dateFormat,
            [Globalization.DateTimeFormatInfo]::CurrentInfo,
            [System.Globalization.DateTimeStyles]::None,
            $outDate
        )
}

# パースした結果受け取り用変数
[datetime]$parsedDate = [datetime]::MinValue

# $rootPath 内のファイルに対して操作
Get-ChildItem -LiteralPath $rootPath |
    # 名前を日付に変換できるものだけにフィルター
    ?{$tryParse.Invoke(
        # $_ には [System.IO.FileSystemInfo] が入るはず
        [IO.Path]::GetFileNameWithoutExtension($_.Name),
        [ref]$parsedDate)
    } |

    # 取得した日付を書式設定した値でグループ化(PowerShellのパイプラインの動作上、$parsedDateはちゃんと反映される)
    Group-Object -Property {$parsedDate.ToString('yyMM')} | 

    # 各グループに対して処理
    %{
        # 出力先のフォルダ作成
        # [IO.Directory]::CreateDirectory は冪等性のある処理っぽいのですでに存在していてもOK
        [string]$destDir = [IO.Path]::Combine($rootPath, $_.Name)
        [IO.Directory]::CreateDirectory($destDir) > $null

        # 各ファイルを移動
        $_.Group | 
        %{
            [string]$moveToPath = [IO.Path]::Combine($destDir,$_.Name)
            Write-Host ('{0} => {1}' -f $_.Name, [IO.Path]::GetFileName($destDir))
            $_.MoveTo($moveToPath)
        }
    }