続・全てのジャンクションを解決したパスを取得したい PSv2版


[前回の記事](http://qiita.com/nukie_53/items/83b4b6fef9b2e5e40dcb "全てのジャンクションを解決したパスを取得したい" )の続きです。

# 問題の確認

># 問題の箇所
>
```ps1
    if ($tmpDir.Attributes -like "*ReparsePoint*")
    {
        $tmpDir=Get-Item $tmpDir.Target
    }
```

PowerShell v2.0 では`[IO.DirectoryInfo]`型に、ジャンクションなどのリンク先を示す`Target`プロパティが追加されていない。
そのため、上記コードでは`$tmpDir.Target`のところで`$null`と評価されうまくいかなかった。


今回はこの箇所を`cmd.exe`の`dir`コマンドを使って強引に解決した。

# 関数を作成する

`Get-Item $tmpDir.Target`と同じような動作を行うものとして、以下のような関数を作成した。


```ps1
Function Solve-Junction #名前がいまいち
{
    param(
        [Parameter(Mandatory=$true)]
        [IO.DirectoryInfo]$InputDir
    )
    
    [String]$junctionName = $InputDir.Name
    [String]$dirTarget    = $InputDir.Fullname + "*"

    [Text.RegularExpressions.Match]$myMatch=
        # ファイル名・属性指定検索
        cmd.exe /c dir $dirTarget /al |

        # [Microsoft.PowerShell.Commands.MatchInfo]→
        Select-String -Pattern "<JUNCTION> +(.+) \[(.+)\]" |

        # [Text.RegularExpressions.Match]→
        ForEach-Object -Process {$_.Matches} |

        # "$_.Groups[1].Value"=(.+)が今のフォルダ名と一致したもののみフィルタリング
        Where-Object -FilterScript {$_.Groups[1].Value -eq $junctionName}
    
    
    #\[(.+)\]の(.+)部分
    Return Get-Item $myMatch.Groups[2].Value
}
```

この関数により「問題の箇所」を以下のように書き換えができる。

```ps1
Get-Item $tmpDir.Target
#↓
Solve-Junction -InputDir $tmpDir
```

# まとめ

## PowerShell

[前回の記事](http://qiita.com/nukie_53/items/83b4b6fef9b2e5e40dcb "全てのジャンクションを解決したパスを取得したい" )のコードに、上記の関数を追加しPowerShell v2.0非対応の部分を省くと以下のようになる。

```ps1:○○.ps1
#引数
[String]$FilePath = $Args[0]

Function Solve-Junction #名前がいまいち
{
    param(
        [Parameter(Mandatory=$true)]
        [IO.DirectoryInfo]$InputDir
    )
    
    [String]$junctionName = $InputDir.Name
    [String]$dirTarget    = $InputDir.Fullname + "*"

    [Text.RegularExpressions.Match]$myMatch=
        # ファイル名・属性指定検索
        cmd.exe /c dir $dirTarget /al |

        # [Microsoft.PowerShell.Commands.MatchInfo]→
        Select-String -Pattern "<JUNCTION> +(.+) \[(.+)\]" |

        # [Text.RegularExpressions.Match]→
        ForEach-Object -Process {$_.Matches} |

        # "$_.Groups[1].Value"=(.+)が今のフォルダ名と一致したもののみフィルタリング
        Where-Object -FilterScript {$_.Groups[1].Value -eq $junctionName}

    #\[(.+)\]の(.+)部分
    Return Get-Item $myMatch.Groups[2].Value
}


[IO.FileSystemInfo]$argPath=Get-Item $FilePath

#一階層毎のフォルダ名配列 Leaf To Root
[Collections.Generic.IList[String]]$DirNameList = New-Object -TypeName Collections.Generic.List[String]


if ($argPath.Attributes -like "*Directory*")
{
    [IO.DirectoryInfo]$tmpDir = $argPath
} else {
    [IO.DirectoryInfo]$tmpDir = $argPath.Directory
}

do
{
    if ($tmpDir.Attributes -like "*ReparsePoint*")
    {
        $tmpDir = Solve-Junction -InputDir $tmpDir
    }
    
    #ジャンクション解決後のフォルダ名を取得する
    $DirNameList.Add($tmpDir.Name)
    
    $tmpDir = $tmpDir.Parent
}
until($tmpDir.FullName -eq $tmpDir.Root.FullName)

#Root To Leaf
$DirNameList.Reverse()


[String]$out = $tmpDir.FullName + ($DirNameList -join '\')

#フォルダを開く
Explorer.exe $out
#結果確認用
#$out
```

## 動作確認用bat

以下の記述のbatファイルで動作を確認。

```bat:○○.bat
@PowerShell.exe -Version 2.0 -ExecutionPolicy RemoteSigned %~dpn0.ps1 '%~1'
```

# あとがき

望む動作のものは出来たが、なんとなくいまいちな感。
具体的には

+ 今回作った関数内のパイプ処理（無理やり感）
+ ファイル属性の判定方法（文字列化して比較）

あたりが気になる。