hack のためのネタ帳, etc,,,

PowerShell、実に気持ち悪い。
Io.FileInfo を使用してみたところ、カレントディレクトリにあるファイルが見つからなかったり、変数に代入出来なくなってしまい、しばらく頭を抱る羽目になったのだが、整理すると以下のような挙動をする事が分かった。
まず、カレントディレクトリにあるファイルが以下のように見つからない。
PS C:\Users\kou> cd a
PS C:\Users\kou\a> dir


    Directory: C:\Users\kou\a


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2017/01/13     11:11              0 x


PS C:\Users\kou\a> $f = [Io.FileInfo] ".\x"
PS C:\Users\kou\a> $f

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
darhsl       1601/01/01      9:00                x


PS C:\Users\kou\a> $f = [Io.FileInfo] "a\x"
PS C:\Users\kou\a> $f

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2017/01/13     11:11              0 x


PS C:\Users\kou\a>

どうも、[Io.FileInfo] がカレントディレクトリを解さないらしい。
C:\Users\kou\a にいるのに、C:\Users\kou (これは PowerShell が起動された際の current directory) が初期状態になっている。
cd の結果が反映されてない。

これ、外部クラスだからなの???
カレントディレクトリをプロセスグローバルではなくクラスローカルに持ってるって事???
とりあえず、以下のように Io.Directory::SetCurrentDirectory() に pwd 渡せば解決するけど、これ cd の度にやるんですか?
PS C:\Users\kou> cd a
PS C:\Users\kou\a>
PS C:\Users\kou\a> ([Io.FileInfo] ".").FullName
C:\Users\kou
PS C:\Users\kou\a> ([Io.DirectoryInfo] ".").FullName
C:\Users\kou
PS C:\Users\kou\a> [Io.DirectoryInfo] "."

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2017/01/13      9:33                kou


PS C:\Users\kou\a> [Io.Directory]::SetCurrentDirectory((pwd).Path)
PS C:\Users\kou\a> ([Io.FileInfo] ".").FullName
C:\Users\kou\a
PS C:\Users\kou\a> ([Io.DirectoryInfo] ".").FullName
C:\Users\kou\a
PS C:\Users\kou\a> [Io.DirectoryInfo] "."

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2017/01/13     11:18                a


PS C:\Users\kou\a>
うわぁ、面倒臭過ぎる。

参考:
以下のようにすると変数 $f に "" を代入した際にエラーとなる。
PS C:\Users\kou\a> $f = [Io.FileInfo] "a\x"
PS C:\Users\kou\a> $f.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     FileInfo                                 System.IO.FileSystemInfo


PS C:\Users\kou\a> $f = ""
PS C:\Users\kou\a> $f.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS C:\Users\kou\a> [Io.FileInfo] $f = "a\x"
PS C:\Users\kou\a> $f.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     FileInfo                                 System.IO.FileSystemInfo


PS C:\Users\kou\a> $f = ""
Cannot convert value "" to type "System.IO.FileInfo". Error: "パスの形式が無効です。"
At line:1 char:1
+ $f = ""
+ ~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ArgumentTransformationMetadataException
    + FullyQualifiedErrorId : RuntimeException

PS C:\Users\kou\a> remove-variable f
PS C:\Users\kou\a> $f = ""
PS C:\Users\kou\a>

どうやら、[クラス名] って書き方にダブルミーニングがあって、上記の動作から推測するに C++ っぽい書き方をすると以下のような対応になる。
[Io.FileInfo] $f         # new Io.FileInfo($f)  って意味
[Io.FileInfo] $f = "a\x" # Io.FileInfo $f("a\x") って意味
自分は、後者の書き方で、$f へは String 型 を代入すると共に、全体としては Io.FileInfo 型を返してほしかったのだが、そうは問屋が卸してくれなかった。
どうもそれは、以下のように書く必要があるらしい。
[Io.FileInfo] ($f = "a\x") # new Io.FileInfo($f = "a\x") って意味

いや、まぁ C 言語でも
int *x = 0; //  x = 0 の代入
*x = 0;     // *x = 0 の代入
みたいなダブルミーニングはあるけどさぁ。

しかし、PowerShell では
更に、$f の初期化の仕方によっては、その後の = オペレーターの挙動が変わってしまっている。
$f = [Io.FileInfo] "a\x" # $f へは任意のオブジェクトを代入可能。言うなれば ::operator=()?
[Io.FileInfo] $f = "a\x" # $f へは Io.FileInfo のみ代入可能。Io.FileInfo::operator=() もしくは右辺が勝手に Io.FileInfo() で括られる?
しかも、これは少なくとも .GetType() メソッドでは区別が付いてない。

この状態は remove-variable で変数を削除すると解除される。

後者のやり方は $f を [Io.FileInfo] 型に固定していると考えると、単純に $f = "" とするのではなく、自動的に $f = Io.FileInfo("") ってしてくれてるとも考える事が出来で、慣れるとこれは結構便利に使えそうな気はするんだけど、慣れないと凄い気持ち悪い。

あと、1liner 的な書き方する時には結構変な挙動があるみたいで、未定義状態から1回目と2回目で全体の型が異なったりする。
PS C:\Users\kou> remove-variable f
PS C:\Users\kou> echo ([Io.FileInfo] $f=".")
.
PS C:\Users\kou> echo ([Io.FileInfo] $f=".")

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2017/01/13      9:33                .


PS C:\Users\kou> remove-variable f
PS C:\Users\kou> ([Io.FileInfo] $f=".").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS C:\Users\kou> ([Io.FileInfo] $f=".").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     FileInfo                                 System.IO.FileSystemInfo


PS C:\Users\kou> remove-variable f
PS C:\Users\kou> [Io.FileInfo] $f="."; $f.getType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     FileInfo                                 System.IO.FileSystemInfo


PS C:\Users\kou> [Io.FileInfo] $f="."; $f.getType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     FileInfo                                 System.IO.FileSystemInfo


PS C:\Users\kou>

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

フリーエリア

管理人/副管理人のみ編集できます