この記事はPowerShell Advent Calendar 2019 の 22 日目です。
野暮用でスクリプトを作っている時にハマった内容です。
小ネタレベルではありますし、普通に作ればハマらない内容ではあると思うのですが、ちょっとした小ネタとして記事を書いておきます。
環境
PSVersion5と6の両方でこの現象は発生するようです。
※PSVersion6はWSL上のPowerShellで確認しています。
経緯
Get-ChildItem $HashTable.Path\*.log
のように、__ハッシュテーブルに入れたファイルパス__と__ファイルを指定するワイルドカード__を並べてをGet-ChildItemを実行すると、Get-ChildItem : 2 番目のパス フラグメントを ドライブ名または UNC 名にすることはできません。
とエラーを吐きました。
PS C:\> $HashTable = @{} #ハッシュテーブルの定義
PS C:\> $HashTable.Add("Path","C:\log") #ハッシュテーブルへ追加
PS C:\> Get-ChildItem $HashTable.Path\*.log #ハッシュテーブル内のパスと[\*.log]を並べてGet-ChildItemを実行
Get-ChildItem : 2 番目のパス フラグメントを ドライブ名または UNC 名にすることはできません。
パラメーター名:path2
発生場所 行:1 文字:1
+ Get-ChildItem $HashTable.Path\*.log
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (C:\log:String) [Get-ChildItem]、ArgumentException
+ FullyQualifiedErrorId : DirArgumentError,Microsoft.PowerShell.Commands.GetChildItemCommand
ハッシュテーブルが悪さをしているのかな?と思い、適当な変数に入れて実行すると、問題なく通ります。
PS C:\> $NonHashTable = $HashTable.Path
PS C:\>
PS C:\> Get-ChildItem $NonHashTable\*.log
ディレクトリ: C:\log
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019/12/20 13:08 0 1.log
変な文字列とかが入っちゃっている?と思って文字列長を調べましたが、結果は同じ文字列長。
PS C:\> $HashTable.Path.Length
6
PS C:\> $NonHashTable.Length
6
ハッシュテーブルが悪いのか?と思い、普通の配列で試してみるも、同じエラーが発生。
PS C:\> $Table = @("C:\log") #テーブルの定義と値の代入
PS C:\> Get-ChildItem $Table[0]\*.log
Get-ChildItem : 2 番目のパス フラグメントを ドライブ名または UNC 名にすることはできません。
パラメーター名:path2
発生場所 行:1 文字:1
+ Get-ChildItem $Table[0]\*.log
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (C:\log:String) [Get-ChildItem]、ArgumentException
+ FullyQualifiedErrorId : DirArgumentError,Microsoft.PowerShell.Commands.GetChildItemCommand
何故…?
原因
Get-ChildItemの引数と指定したパスをWrite-Hostで表示させてみたところ、
ハッシュテーブル/配列問わず、文字列の後に__謎の空白__が入っていることを確認しました。
そのため、Get-ChildItemに二つのパスが渡されている状態となり、エラーが発生していたようです。
PS C:\> Write-Host $NonHashTable\*.log
C:\log\*.log #正しく繋がっている
PS C:\> Write-Host $HashTable.Path\*.log
C:\log \*.log #間に空白がある…!
PS C:\> Write-Host $Table[0]\*.log
C:\log \*.log #間に空白がある…!
対策
そもそも、$HashTable.Path\*.log
という書き方は美しくないよね、ということもあり、($HashTable.Path + "\*.log")
と文字列を結合するように書き換えることで回避しました。
PS C:\> Get-ChildItem ($HashTable.Path + "\*.log")
ディレクトリ: C:\log
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2019/12/20 13:08 0 1.log
余談
-
$HashTable.Path\*.log
を__文字列を結合__と称するのは何か違う気がしたので、__文字列を並べる__としました。 - 特にファイルパスを変数で取り扱うとき、「\」で変数名が丁度よく区切れるので並べて書いてしまう傾向があるのですが、それが良くなかったように思えます。
###良くない書き方($date部分が通常の変数では問題ないけど、配列になるとエラーになる)
Get-ChildItem C:\log\$date\*.log
###少し気を付けた書き方
Get-ChildItem ("C:\log\" + $date + "\*.log")