しばたテックブログ

気分で書いている技術ブログです。

WMF 5.1の新機能をざっくり説明する - 3. コンソール機能強化 編

今回は

  1. 新シナリオと機能 編
  2. バグ修正 編

の続きです。

今回の対象範囲

WMF 5.1 のコンソール機能強化 | Microsoft Docs

についてわかる範囲で補足を入れていく感じにします。

VT100 のサポート

Windows 10 では、VT100 エスケープ シーケンスのサポートが追加されました。
PowerShell は、テーブルの幅を計算するとき、特定の VT100 書式指定エスケープ シーケンスを無視します。

について、VT100エスケープシーケンスはTH2(1511)から利用可能なのですがデフォルトで無効になっています。
来月のAnniversary Update(RS1)でデフォルト有効になる様です。

TH2でエスケープシーケンスを有効にするには以下のコードを実行します。

#
# Windows 10 TH2(1511)以降でのみ有効
#
# Win32 API定義をAdd-Typeする
Add-Type -MemberDefinition @"
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr GetStdHandle(int handle);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool GetConsoleMode(IntPtr handle, out int mode);
"@ -Namespace Win32 -Name NativeMethods
# コンソールモードを変更
$Handle = [Win32.NativeMethods]::GetStdHandle(-11) #  stdout
$Mode = 0
$Result = [Win32.NativeMethods]::GetConsoleMode($Handle, [ref]$Mode)
$Mode = $Mode -bor 4 # undocumented flag to enable ansi/vt100
$Result = [Win32.NativeMethods]::SetConsoleMode($Handle, $Mode)

この辺の話は、

stknohg.hatenablog.jp

でも触れているので参考にしてください。
尚、

VT100 エスケープ シーケンスは、Windows 10 Anniversary 更新以降でのみサポートされることに注意してください。
それより前のシステムではサポートされません。

とあるのでTH2では機能は使えどもサポートされない様です。

MSDNに記載されているサンプルを試してみると以下の様な感じになります。

if ($host.UI.SupportsVirtualTerminal)
{
    $esc = [char]0x1b
    "A yellow ${esc}[93mhello${esc}[0m"
}else{
    "A default hello"
}

WMF 5.0(通常)

f:id:stknohg:20160731013641p:plain

WMF 5.1

f:id:stknohg:20160731013648p:plain

WMF 5.0(エスケープシーケンス有効化)

f:id:stknohg:20160731013656p:plain

補足として、$host.UI.SupportsVirtualTerminalについてはWMF 5.1から利用可能なプロパティですので、WMF 5.0でエスケープシーケンスを有効にしても値を取得することはできません。

PSReadline での vi モードのサポート

これはWMF 5.1ではなくPSReadline 1.2の新機能になります。

WMF 5.0以前でもPSReadline 1.2にすればviモードを利用することができます。
WMF 5.1に搭載されるPSReadlineが1.2になるのでここに載っている感じでしょうか。

vi モードへの変更にはSet-PSReadlineOptionコマンドレットを使い以下の様にします。

# 変更するとき
Set-PSReadlineOption -EditMode Vi

元に戻すときはセッションを終了するか以下の様にします。

# 元に戻すとき
Set-PSReadlineOption -EditMode Windows

私はviちからがほとんどないので断言はできないのですが、初期状態が挿入モードだったり、モード変更にCtrl+[が使えなかったり*1と、「これ本当に使えるのか?」という使用感でした。
コンソール上の入力欄は基本1行なのでそこでviのモード変更というのは相性が悪い気がしています。

このあたりviに強い方の使用感を聞いてみたいですね。

対話型の入力を使用する stdin のリダイレクト

地味にうれしい改善です。

あまり知られていない機能ですが、PowerShellで対話型の入力を標準入力からリダイレクトするにはpowershell.exe -File -とする必要がありました。
使いどころを説明するのが難しいのですが、過去に、

stknohg.hatenablog.jp

のエントリで、OpenSSHのSSHdにログインした後にPowerShellを起動する際に使ったりしています。

この引数がWMF 5.1からは不要になるということです。

PSReadline は現在はリダイレクトされた stdin をサポートせず、リダイレクトされた stdin での組み込みコマンド ライン編集エクスペリエンスは非常に限られたものであることに注意してください (たとえば、方向キーは機能しません)。
PSReadline の将来のリリースではこの問題が対処されるはずです。

については、現状powershell.exe -File -をした場合はPSReadlineは使えないといった程度の認識でよいかと思います。

リンク

他の回のリンクはこちら。
その1その2その4

*1:こちらはキーバインドをカスタマイズすれば対処できそうですが..

WMF 5.1の新機能をざっくり説明する - 2. バグ修正 編

前回の続きです。

今回の対象範囲

今回は、

WMF 5.1 のバグ修正

についてわかる範囲で補足を入れていく感じにします。

モジュールの自動検出の完全な受け入れ

TODO : もう少し調べる

MSDNを読んでもいまいちなんのためのバグ修正なのかわかりませんでした...

モジュールの自動検出順が、

  • PowerShell 3.0 ~ 5.0 : $PSHome\Modules$env:PSModulePath の順
  • PowerShell 5.1 ~ : $env:PSModulePath のみ

になったということでしょうか?

また、

これにより、PowerShell によって提供されるコマンド (Get-ChildItem など) を定義するユーザー作成のモジュールを、自動的に読み込み、組み込みコマンドを正しくオーバーライドできます。

との事なのでPowerShell標準のコマンドレットをカスタマイズしやすくするための改善の様です。

ファイルのリダイレクトの非ハードコード化

こちらはわかりやすく、かつ結構な人が望んでいた改善ではないでしょうか。

これまでのPowerShellでは>(>>)演算子を使ってファイルリダイレクトした場合のエンコーディングUnicode固定でした。

これは、>(>>)演算子は内部的にOut-Fileコマンドレットに置き換えられるのですが、その際の引数に-Endoding Unicodeがハードコーディングされていたために起きていました。
PowerShell 5.0だとSystem.Management.Automation.FileRedirectionクラスがOut-Fileコマンドレットへの置き換えをになっている様で、ILSpyで中身を見ると、

// WMF 5.0 (Windows 10)
internal Pipe GetRedirectionPipe(ExecutionContext context, PipelineProcessor parentPipelineProcessor)
{

    // ・・・(中略)・・・

    CommandProcessorBase commandProcessorBase = context.CreateCommand("out-file", false);
    CommandParameterInternal parameter = CommandParameterInternal.CreateParameterWithArgument(PositionUtilities.EmptyExtent, "Encoding", "-Encoding:", PositionUtilities.EmptyExtent, "Unicode", false, false);
    commandProcessorBase.AddParameter(parameter);
    if (this.Appending)
    {
        parameter = CommandParameterInternal.CreateParameterWithArgument(PositionUtilities.EmptyExtent, "Append", "-Append:", PositionUtilities.EmptyExtent, true, false, false);
        commandProcessorBase.AddParameter(parameter);
    }
    parameter = CommandParameterInternal.CreateParameterWithArgument(PositionUtilities.EmptyExtent, "Filepath", "-Filepath:", PositionUtilities.EmptyExtent, this.File, false, false);
    commandProcessorBase.AddParameter(parameter);
    this.PipelineProcessor = new PipelineProcessor();
    this.PipelineProcessor.Add(commandProcessorBase);
    
    // ・・・(後略)・・・
}

な感じで-Endodingパラメーターがハードコードされています。

そして、WMF 5.1ではこの-Endoding Unicodeのハードコードが取り除かれました。
System.Management.Automation.FileRedirectionクラスを見ると、

// WMF 5.1 (Windows 10 Insider Preview)
internal Pipe GetRedirectionPipe(ExecutionContext context, PipelineProcessor parentPipelineProcessor)
{

    // ・・・(中略)・・・
        
    CommandProcessorBase commandProcessorBase = context.CreateCommand("out-file", false);
    CommandParameterInternal parameter = CommandParameterInternal.CreateParameterWithArgument(PositionUtilities.EmptyExtent, "Filepath", "-Filepath:", PositionUtilities.EmptyExtent, this.File, false, false);
    commandProcessorBase.AddParameter(parameter);
    if (this.Appending)
    {
        parameter = CommandParameterInternal.CreateParameterWithArgument(PositionUtilities.EmptyExtent, "Append", "-Append:", PositionUtilities.EmptyExtent, true, false, false);
        commandProcessorBase.AddParameter(parameter);
    }
    this.PipelineProcessor = new PipelineProcessor();
    this.PipelineProcessor.Add(commandProcessorBase);
    
    // ・・・(後略)・・・
}

と、

CommandParameterInternal parameter = CommandParameterInternal.CreateParameterWithArgument(PositionUtilities.EmptyExtent, "Encoding", "-Encoding:", PositionUtilities.EmptyExtent, "Unicode", false, false);

の行が消えているのが確認できます。

この修正により、WMF 5.1からは$PSDefaultParameterValues変数を以下の様に設定することで、>(>>)演算子を使ったリダイレクトのエンコーディングを変えることができます。

$PSDefaultParameterValues["Out-File:Encoding"] = "Ascii"

なお、$PSDefaultParameterValues自体はPowerShell 3.0から導入された、コマンドレットを呼ぶ際の引数のデフォルト値を制御するための機能になります。

$PSDefaultParameterValues["[コマンドレット名]:[パラメーター名]"] = [デフォルト値]の形式でコマンドレットのデフォルト値を制御することができます。
詳細はabout_Parameters_Default_Valuesを参照してください。

メンバーへのアクセスでのバグ再発の修正

こちらはMSDNの説明の通りでしょう。

WMF 5.0 で新しく発生したバグにより、System.Reflection.RuntimeType のメンバーにアクセスできませんでした ([int].ImplementedInterfaces など)。 WMF5.1 ではこのバグが修正されています。

[int].ImplementedInterfacesで試してみると、以下の様にWMF 5.0では値をとれなかったのがWMF 5.1では正しくとれる様に改善されます。

# WMF 5.0
PS C:\> [int].ImplementedInterfaces
PS C:\>

# WMF 5.1
PS C:\> [int].ImplementedInterfaces

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    IComparable
True     False    IFormattable
True     False    IConvertible
True     False    IComparable`1
True     False    IEquatable`1

ただ、この改善がどれくらいの人にとってうれしいのかはよくわかりません...

COM オブジェクトでのいくつかの問題の修正

こちらもMSDNの説明の通り、とくに触れることは無いでしょう。

WMF 5.0 では、COM オブジェクト上のメソッドを呼び出して COM オブジェクトのプロパティにアクセスする新しい COM バインダーが導入されました。
この新しいバインダーによりパフォーマンスが大幅に向上しましたが、バグもいくつか含まれていました。
WMF5.1 ではそれが修正されました。

例として出されているコードは、WMF 5.0、5.1で実行すると以下の様になります。  

# WMF 5.0
PS C:\> $obj = new-object -com wscript.shell
PS C:\> $obj.SendKeys([char]173)
PS C:\> 173

# WMF 5.1
PS C:\> $obj = new-object -com wscript.shell
PS C:\> $obj.SendKeys([char]173)
PS C:\> -

ちなみにWMF 5.0でこの例を正しく動かすには以下の様に明示的に[string]にキャストする必要があります。

# WMF 5.0
PS C:\> $obj = new-object -com wscript.shell
PS C:\> $obj.SendKeys([string][char]173)
PS C:\> -

[ordered] がクラス内で許可されなかった

WMF 5.1ではクラス内で[ordered]が使える様になりました。

WMF5 では、クラスで使用されるリテラル形を検証するクラスが導入されました。
[ordered] はリテラル形のように見えますが、真の .Net 型ではありません。
WMF5 は、クラスの内の [ordered] で誤ってエラーを報告しました。

について、[ordered]PowerShell言語仕様書にも、

4.3.9 The ordered type
Type ordered is a pseudo type used only for conversions.

とあり、型変換のためだけに使う疑似型であることが明記されています。

WMF 5.0では[ordered]という特例に対する考慮が漏れていた(もしくは後回しにした)といった感じでしょうか。

複数のバージョンがあるトピックについてのヘルプが機能しない

TODO : もう少し調べる

こちらについては検証環境を作ることができず検証ができませんでした...

とりあえずWMF 5.1では、

WMF 5.1 では、最新バージョンのトピックのヘルプを返すことでこれが解決されています。

だけ押さえておけば良いと思います。

リンク

他の回のリンクはこちら。
その1その3その4

WMF 5.1の新機能をざっくり説明する - 1. 新シナリオと機能 編

WMF 5.1のプレビュー版も出たので、そろそろWMF 5.1の機能について触れないとダメかなと思いエントリを書きます。

一回では書ききれないので何回かに分けて書く予定です。

公式情報

WMF 5.1についてはMSDNの、

WMF 5.1 リリース ノート

でリリースノートが出ており、公式な情報はここから入手できます。

今回の対象範囲

今回は、

WMF 5.1 の新しいシナリオと機能

についてわかる範囲で補足を入れていく感じにします。

PowerShell のエディション

これは、 Nano ServerのPowerShellについてざっくりとした話でも触れましたがMSDNの説明の通りですね。

  • デスクトップ エディション: .NET Framework 上に構築されており、Server Core や Windows Desktop などの Windows の完全エディションで実行する PowerShell のバージョンを対象とするスクリプトおよびモジュールとの互換性を提供します。

  • コア エディション: .NET Core 上に構築されており、Nano Server や Windows IoT などの Windows の縮小エディションで実行する PowerShell のバージョンを対象とするスクリプトおよびモジュールとの互換性を提供します。

続いて、以下の項目についてはまだドキュメントが更新されていませんのでわかる範囲でざっと触れていきます。

PowerShell の実行エディションを特定する

過去のエントリで何回か触れていますが、PowerShellのエディションは$PSVersionTable.PSEditionで判定できます。

# PowerShell 5.1以降
PS C:\> $PSVersionTable.PSEdition
Desktop

文字列型で、DesktopエディションならDesktop、CoreエディションならCoreを返します。
そして、PowerShell 5.0以前にはこのプロパティはありませんので$nullか例外を返すことになります。

# PowerShell 1.0 ~ 5.0
PS C:\> $PSVersionTable.PSEdition
PS C:\>

# StrictMode を設定している場合は例外を返す場合がある
PS C:\> Set-StrictMode -Version 2.0
PS C:\> $PSVersionTable.PSEdition
このオブジェクトにプロパティ 'PSEdition' が見つかりません。プロパティが存在することを確認してください。
発生場所 行:1 文字:1
+ $PSVersionTable.PSEdition
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict

これらをまとめると、

PowerShellのバージョン エディション 判定方法
1.0 ~ 5.0 (便宜上)Desktop $PSVersionTable.PSEdition = $null または プロパティアクセス時に例外
5.1 ~ Desktop $PSVersionTable.PSEdition = "Desktop"
5.1 ~ Core $PSVersionTable.PSEdition = "Core"

となります。

特定の PowerShell バージョンに対するモジュールの互換性を宣言する

TODO : まだよくわかりません。わかったら追記します。
新しいドキュメントではこの項目自体が消滅していました...

CompatiblePSEditions で Get-Module の結果をフィルターする

こちらはNano ServerのPowerShellについてざっくりとした話でも触れた通り以下の様にしてフィルタできます。

Get-Module -ListAvailable | ? CompatiblePSEditions -Contains "Desktop"

ただ、現時点ではCompatiblePSEditions = $nullの様で、今後どうなるかはよくわかりません
結局既存のモジュールについてはCompatiblePSEditions = $nullのままとなりました。

互換性のある PowerShell のエディションで実行しない場合はスクリプトを実行させない

こちらもNano ServerのPowerShellについてざっくりとした話でも触れた様に#requires -PSEditionステートメントで実行可能なエディションを制限することができます。

以下の簡単なサンプルで試してみます。

DesktopOnlySample.ps1

#requires -PSEdition Desktop

Write-Output "これはDesktopエディション専用です!"

CoreOnlySample.ps1

#requires -PSEdition Core

Write-Output "これはCoreエディション専用です!"

実行結果(Windows 10 Insider Preview(Desktopエディション)で試した結果)

f:id:stknohg:20160730182021p:plain

確かにエディションが制限されていることがわかります。

カタログ コマンドレット

WMF 5.1ではコマンドレットからセキュリティカタログファイルの作成と検証ができる様になりました。

セキュリティカタログファイルについてざっくり説明すると、指定されたフォルダやファイルのハッシュを1つのファイル(.cat)にまとめたもので改竄検知に利用します。
デバイスドライバの開発によく使われる様ですが、わたしは門外漢なのでその辺はよくわかりません。

New-FileCatalogでカタログファイルを作成、Test-FileCatalogでカタログの検証を行います。
細かい説明はMSDNの内容を参照してください。

現時点では、日本語版の画像が存在しておらずリンク切れになっているのでこのエントリで試した結果を補足しておきます。

# カタログファイルの作成
New-FileCatalog -Path 'C:\Program Files\WindowsPowerShell\Modules\Pester\' -CatalogFilePath Pester.cat -CatalogVersion 2.0

作成結果

f:id:stknohg:20160730182151j:plain

できたカタログファイル

f:id:stknohg:20160730182219j:plain

f:id:stknohg:20160730182230j:plain

# カタログファイルの検証
Test-FileCatalog -Path 'C:\Program Files\WindowsPowerShell\Modules\Pester\' -CatalogFilePath Pester.cat -Detailed

検証結果

f:id:stknohg:20160730182310j:plain

ちなみに、該当フォルダを適当に変えてみるとTest-FileCatalogの結果がValidationFailedとなりきちんと改竄を検知できることがわかります。

f:id:stknohg:20160730183529p:plain

f:id:stknohg:20160730183716p:plain

モジュール分析キャッシュ

こちらについては...MSDNに書いてある通りなのでしょう...

PowerShellはあまり内部構造を公開していないので、この機能がどういった場合にうれしいのかちょっとわからない感じです。

キャッシュ機構についてはPowerShell 5.1以前でも${env:LOCALAPPDATA}\Microsoft\Windows\PowerShell\CommandAnalysisというフォルダがあり、このフォルダの内部に大量のキャッシュと思しきファイルが存在しています。
PowerShell 5.1ではこのキャッシュ機構が見直され、ユーザーが介在する余地が増えたと捉えるのが妥当でしょうか。

いまのところはPSModuleAnalysisCachePathPSDisableModuleAnalysisCacheCleanup環境変数があることを知っておけば十分な気がします。
トラブルシューティング時に利用する機会があるかもしれません。

モジュールのバージョンの指定

まったくドキュメント化されていない*1のですが、using moduleステートメント自体はWMF 5.0から存在していました。

using module [モジュール名]

Import-Moduleと同等のことができる機能です。

これは、

WMF 5.1 では、using module は PowerShell の他のモジュール関連構造と同様に動作します。 以前は、モジュールの特定のバージョンを指定する方法はありませんでした。
複数のバージョンが存在する場合、エラーが発生しました。

にある通りWMF 5.0ではバージョン指定はできません。
以下の例の様にPSReadline1.11.2が共存しているときにusing moduleを実行するとエラーとなります。

f:id:stknohg:20160730184607p:plain

f:id:stknohg:20160730184614p:plain

これがWMF 5.1では

using module @{ModuleName = 'PSReadLine'; RequiredVersion = '1.1'}

の様にしてバージョン指定可能になります。

実行結果はこんな感じです。

f:id:stknohg:20160730190227p:plain

また、バージョンを指定しない場合は最新バージョンがロードされます。

f:id:stknohg:20160730190400p:plain

Pester の機能強化

これはMSDNの通りですね。
細かい変更点はCHANGELOGを参照してください。

リンク

他の回のリンクはこちら。
その2その3その4

*1:激おこですよ