しばたテックブログ

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

PowerShell Core 6.1がリリースされました

公式情報はコチラ

blogs.msdn.microsoft.com

本エントリでは公式情報をベースに補足を入れる形で説明していきます。

新機能について

公式ブログではPowerShell Core 6.1の新機能として以下の点を挙げています。

1. Windows 10およびWindows Server 2019との互換性強化

公式ブログで、

Compatibility with 1900+ existing cmdlets in Windows 10 and Windows Server 2019

と説明されている様にBuild 17711以降のWindows 10およびWindows Server 2019に対する互換性が強化されています。
具体的にどの点を修正しているかまでは公開されていませんが、PowerShell Core側、OS側両方に修正が入っているものと思われます。

この互換性の強化については、以前に

blogs.msdn.microsoft.com

で説明されており、詳細はコチラのエントリを見ると良いでしょう。


【2018/10/01追記】

この点について別エントリでまとめました。
こちらも併せてご覧ください。

blog.shibata.tech

【追記ここまで】

2. .NET Core 2.1化

PowerShell Core 6.0がリリースされた時のアプリケーション基盤は.NET Core 2.0(ラインタイムバージョンで2.0.4)でしたが、PowerShell Core 6.1では.NET Core 2.1(ラインタイムバージョン2.1.3)に更新されています。

.NET Core 2.1の新機能はコチラに記載されています。

PowerShell Coreとしては主にセキュリティ更新やパフォーマンス改善の部分で恩恵を受けています。

3. サポートプラットフォームの最新化

サポートされるプラットフォームがPowerShell Core 6.0の頃に対して最新化されています。
同時に古いプラットフォームがいくつかEOLとなっています。

現時点でサポートされるプラットフォームは以下。

  • Windows 7/8.1/10
  • Windows Server 2008R2/2012/2012R2/2016 (and 2019 on release)
  • Windows Server Semi-Annual Channel (SAC)
  • macOS 10.12+
  • Ubuntu 14.04/16.04/18.04
  • Debian 8.7+/9
  • CentOS 7
  • Red Hat Enterprise Linux (RHEL) 7
  • openSUSE 42.3
  • Fedora 27/28

以下は非公式のコミュニティサポートのみ

  • Ubuntu 18.10
  • Arch Linux
  • Raspbian (ARM32)
  • Kali Linux
  • Alpine (experimental Docker image coming soon)

以下のLinuxディストリビューションについてはディストリビューション自体がEOLとなったためPowerShell Coreとしてもサポート外となりました。

  • Ubuntu 17.04
  • Fedora 25,26
  • openSUSE 42.2

ちなみに、PowerShell Core 6.0リリースの際に説明した通り、PowerShell Core のサポート ライフサイクルはMicrosoft Modern Lifecycle Policyとなります。

docs.microsoft.com

4. パフォーマンス改善

先述の.NET 2.1化による影響を主としてパフォーマンスが改善されています。
公式ブログでは"Significant"と謳っていますが具体的なベンチマークが公開されているわけではないのでほどほどに受け止めておくと良いでしょう。
(ただし、開発環境に対してパフォーマンス測定ツールが導入されており内部的な計測はされている様です)


【2018/09/16 追記】

いくつかのコマンドレットに対するパフォーマンス計測結果がDocsに記載されていました。

詳細はこちらをご覧ください。

【追記ここまで】


私もすべての改善点を挙げることはできませんが、内部的に.NET Core 2.1の新機能であるSpan<T>を使う修正が入るなどランタイムだけではなくコードベースの改善も幾つかなされています。

以下に幾つかパフォーマンス改善に関するプルリクエストを列記しておきます。

5. Markdown cmdlets

Markdownを扱うコマンドレットが追加されました。
細かい話は

blog.shibata.tech

をご覧ください。

6. Experimental feature flags

試験的な機能追加に備えるための機能が追加されました。
仕様についてはコチラのRFCをご覧ください。

現時点で採用された試験的な機能は無いためこれが役に立つのはもうしばらく先の話となります。

仕組みについてですが、基本的にはpowershell.config.jsonに利用する試験的な機能を記述する形になります。

// RFCのサンプルを例示
{
  "ExperimentalFeatures": [
    "A-Experimental-Feature-name",
    "Another-Experimental-Feature-Name"
  ]
}

機能の側はコマンドレットにExperimental属性を付けることで有効・無効化の対象となります。

// RFCのサンプルを一部改変して例示
[Experimental("A-Experimental-Feature-name", ExperimentAction.Show)]
[Cmdlet(Verbs.Invoke, "WebRequest")]
public class InvokeWebRequestCommandV2 : WebCmdletBaseV2 { ... }

[Experimental("A-Experimental-Feature-name", ExperimentAction.Hide)]
[Cmdlet(Verbs.Invoke, "WebRequest")]
public class InvokeWebRequestCommand : WebCmdletBase { ... }

その他

その他新機能の詳細についてはDocsのリリースノートとGitHubのチェンジログを見るのが一番です。
本ブログでも近いうちにこの内容をまとめたいと思います。

また、公式ブログで触れられていないものの影響が大きそうな点として

あたりが挙げられます。


【2018/10/16追記】

Docsにある新機能・破壊的変更について別エントリでまとめました。

blog.shibata.tech

こちらも併せてご覧ください。

【追記ここまで】

PowerShellの今後について

公式ブログでは今後の方針について一切触れられませんでした。

あくまで私の予測ですが、今後しばらくは現状維持となりWindows 10およびWindows Server 2019との互換性の強化や不具合の改善につとめていくのかな?と思います。

PowerShell Core 6.1で導入されるMarkdown関連機能について

PowerShell Core 6.1 Preview.4からMarkdownを扱う以下のコマンドレットが追加されました。

  • ConvertFrom-Markdown
  • Show-Markdown
  • Get-MarkdownOption
  • Set-MarkdownOption

これらのコマンドレットを使うとMarkdownのドキュメントを解析し、HTMLまたはコンソールに表示することができる様になります。

実装方法は前回説明したThreadJobとは異なりPowerShell Core本体(Microsoft.PowerShell.Utility.dll)に実装されています。
このためWindows PowerShellでこれらのコマンドを利用することはできません。

また内部的にMarkdigを利用しています。

github.com

機能説明

まずは各コマンドの機能説明をします。

ConvertFrom-Markdown

Markdownのファイルまたは文字列を読み込みMicrosoft.PowerShell.MarkdownRender.MarkdownInfo型のオブジェクトに変換します。
Microsoft.PowerShell.MarkdownRender.MarkdownInfo型のオブジェクトは後述のShow-MarkdownでHTMLまたはコンソールに表示します。

-Pathおよび-LiteralPathを指定した場合はファイルから内容を読み込みます。
文字コードはUTF-8であることが期待されており-Encodingパラメーターはありません。

ConvertFrom-Markdown -Path .\sample.md

(sample.mdの内容は以下)

# ヘッダ1

__マークダウン__ の _サンプル_ です。

## ヘッダ2

* Hello
* World

### ヘッダ3

1. Hello
1. PowerShell

#### ヘッダ4

うちのブログ

* [しばたテックブログ](https://blog.shibata.tech) 

##### ヘッダ5

\```
# コードブロックのサンプル
Write-Output "Hello"
\```

###### ヘッダ6

> 引用文です。

既定ではMarkdownはHTMLに変換されます。

f:id:stknohg:20180910225601p:plain

-AsVT100EncodedStringパラメーターを付けるとコンソール表示用にVT100エスケープシーケンスで修飾された文字列に変換されます。

ConvertFrom-Markdown -Path .\sample.md -AsVT100EncodedString

f:id:stknohg:20180910225618p:plain

また、パイプラインから文字列およびSystem.IO.FileInfo型のオブジェクトを受け取ることも可能です。

@"
# ヘッダ1

__マークダウン__ の _サンプル_ です。

## ヘッダ2

* Hello
* World
"@ | ConvertFrom-Markdown

f:id:stknohg:20180910225631p:plain

Get-ChildItem .\sample.md | ConvertFrom-Markdown

f:id:stknohg:20180910231819p:plain

Show-Markdown

ConvertFrom-Markdownで生成したオブジェクトをコンソールもしくはWEBブラウザに表示します。

個人的にちょっとイケてないと思うのですが、このコマンドはHTMLが既定のConvertFrom-Markdownとは逆でコンソール表示が既定となります。
このため以下の様にConvertFrom-MarkdownShow-Markdownの組み合わせでNGとなるケースが出てしまいます。

# OK
ConvertFrom-Markdown -Path .\sample.md -AsVT100EncodedString | Show-Markdown

# NG : Show-Markdownは既定でコンソール表示なため、ConvertFrom-Markdownで-AsVT100EncodedStringの指定が必要
ConvertFrom-Markdown -Path .\sample.md | Show-Markdown

f:id:stknohg:20180910225652p:plain

f:id:stknohg:20180910225702p:plain

HTMLを表示する場合は-UseBrowserパラメーターを指定してやる必要があり、名前の通り既定のブラウザでHTMLとなったMarkdownの文章を表示します。

# OK
ConvertFrom-Markdown -Path .\sample.md | Show-Markdown -UseBrowser

f:id:stknohg:20180910225713p:plain

f:id:stknohg:20180910225723p:plain


【2018/09/11追記】

最初にPreview.4でこの機能がリリースされてからShow-Markdownが使いにくいというフィードバックがあり、次のリリースのRC.1ではShow-Markdown-InputObject-Path-LiteralPathパラメーターが追加され、このコマンドで直接Markdownを扱うことができる様に改善されています。

github.com

# PowerShell Core 6.1 RC.1より
Show-Markdown -Path .\sample.md

f:id:stknohg:20180911125903p:plain

# PowerShell Core 6.1 RC.1より
@"
# ヘッダ1

__マークダウン__ の _サンプル_ です。

## ヘッダ2

* Hello
* World
"@ | Show-Markdown

f:id:stknohg:20180911130059p:plain

【追記ここまで】

Get-MarkdownOption

Markdownの変換に関する各種設定を表示します。

Get-MarkdownOption

現時点ではMarkdownの各要素をコンソール表示する際の色指定のみ存在します。
今後設定が増える可能性は高いと思われます。

f:id:stknohg:20180910225734p:plain

Set-MarkdownOption

Markdownの変換に関する各種設定を設定します。

Set-MarkdownOption -Header1Color "[4;92m"

f:id:stknohg:20180910225747p:plain

PowerShellではありがちですが、このコマンドで設定した内容は永続化されません。
永続化したい場合はプロファイルにコマンドを記述してください。

何故この機能が追加されることになったのか?

ここからは補足的な内容となります。
興味のある方はご覧ください。

今回追加されたMarkdownに関わる機能は以下Issueを発端とし、

github.com

RFC(Native Markdown Rendering)を経て採用、機能追加されています。*1

github.com

このRFCによれば、

Motivation

Markdown is a common authoring format used by the community. There is no easy way in PowerShell to visualize a markdown document on console. Since the PowerShell help is authored in markdown, these components will be used for rendering help content.

とあり、PowerShell TeamとしてはMarkdownで新しくヘルプドキュメントを作り直したい展望があり、そのためにPowerShell内部でネイティブにMarkdownを解析しレンダリングする機能が必要になったことが根底にあります。

すこし話が逸れますが、PowerShellのヘルプドキュメントはコードコメントかMAMLで書く必要があり、特にMAMLの記述が非常に面倒であるという現状があります。
(私もMAMLは全然書けませんし、正直書く気も起きません...)

この現状を打開するためにPlatyPSといったプロジェクトが存在し、ヘルプドキュメントのMarkdown化は以前から検討されていました。

github.com

まだヘルプドキュメントのMarkdown化は実現していませんが、今回の機能追加を経て順次成されていくのかな?と思います。

現状の課題と展望

先述のRFCを見ればわかりますが、現在はRFCにある機能が全て実装されていませんし、実装されている機能にも幾つか細かいバグがあります。

他にも

「ConvertFrom-MarkdownがあるならConvertTo-Markdownも欲しい。」

といった要望や、

「Markdown関連の機能をモジュールとして切り出すべきでは?」

といった意見もあり、まだまだ課題が多いのが正直なところでしょう。

これらの課題がどう解決されるかはまだ何とも言えません。

最後に

ちょっと長くなりましたがこんな感じです。

機能のあり方に課題は残るものの、利用者目線で見ればこの点を気にする必要は無いでしょう。
PowerShell Teamの思惑を抜きにしてもMarkdownはよく使うものですし追加された機能を活用できる状況は多いのではないかと思います。

*1:RFCを採用したなら3-Experimentalから4-Experimental-Acceptedに移動すべきなのですが、なぜか移動されておらずその理由も不明です... 2018/0913に5-Finalに移動されてました。

PowerShell Core 6.1で導入されるThreadJobについて

ちょっと前の話なのですが、PowerShell Core 6.1 Preview.4からジョブ関連でThreadJobという新機能が追加されました。

従来のジョブ

これまでのPowerShellではStart-Jobなどのコマンドを使うことで重たい処理をバックグラウンドで実行させるジョブの仕組みが提供されてきました。
PowerShell Core 6.0からはStart-Jobの代わりに&を使うこともできます。

blog.shibata.tech

このジョブの仕組みは、個々のジョブをPowerShellのプロセスとして実行するものであるため、ジョブの独立性には優れているものの、複数のジョブを同時に並列して実行する様な場合はプロセス起動のコストの高さからパフォーマンスが急激に悪くなってしまう問題を抱えていました。

f:id:stknohg:20180905174141p:plain

(PowerShellのジョブ実行の仕組み)

例として以下の様な単純なジョブでも10並列にすると異常に時間がかかってしまいます。

Measure-Command {
    # 10並列でジョブ実行
    1..10 | ForEach-Object {
        # 与えられた引数を表示するだけの単純なジョブを実行
        Start-Job -ArgumentList $_ -ScriptBlock { param($arg) Write-Output "arg = $arg" }
    }
    # ジョブの終了待ち
    Get-Job | Receive-Job -Wait -OutVariable Output
}
# 個々のジョブの処理時間を表示
Get-Job | Select-Object Id, Name, PSBeginTime, PSEndTime

手元の環境(Surface Pro4)で約22秒もかかっています。

f:id:stknohg:20180905174308p:plain

ちなみにこの処理を実行しているときはこんな感じで大量のPowerShellプロセスが起動されリソースを消費していることが分かります。

f:id:stknohg:20180905174415p:plain

ThreadJob

この問題を解消するために導入されたのがThreadJobになります。

こちらは中の人であるPaulさんのモジュールとして機能が実装され、PowerShell Core 6.1 Preview.4より標準に組み込まれる形になっています。

github.com

このモジュールで提供される機能はStart-ThreadJobコマンドただ一つです。
その使い方は基本的にはStart-Jobと同じで、Start-ThreadJobコマンドでジョブが作られ、作られたジョブはGet-JobReceive-JobRemove-Jobといったコマンドで扱うことができます。

f:id:stknohg:20180905174450p:plain

従来のジョブと違うのはPSJobTypeNameの内容がThreadJobとなっているくらいでしょうか。

ThreadJobの仕組み

ThreadJobでは個々のジョブは別プロセスとはならず、同一プロセス内でRunspaceを分けることで行われています。

f:id:stknohg:20180905174508p:plain

(ThreadJobの仕組み)

このRunspaceを分けることで並列処理を行う手法自体は昔からあるもので、詳細については以下のブログを見ると参考になるでしょう。

ThreadJobはこの並列処理の手法をPowerShellのジョブのインターフェイスに合わせたものとなります。

性能差

ThreadJobは同一プロセス内の別Runspaceで実行されるため従来のジョブほどの独立性はありませんが、起動コストが少なく複数のジョブを並列実行するのに向いています。

最初に示したジョブの例をThreadJobで試してみると、約0.3秒と1秒もかからずに終わってしまいます。

Measure-Command {
    # 10並列でThreadJob実行
    1..10 | ForEach-Object {
        # 与えられた引数を表示するだけの単純なジョブを実行
        Start-ThreadJob -ArgumentList $_ -ScriptBlock { param($arg) Write-Output "arg = $arg" }
    }
    # ジョブの終了待ち
    Get-Job | Receive-Job -Wait -OutVariable Output
}
# 個々のジョブの処理時間を表示
Get-Job | Select-Object Id, Name, PSBeginTime, PSEndTime

f:id:stknohg:20180905174617p:plain

Windows PowerShellでのThreadJobの利用

このTreadJobはモジュールとして提供されているため、PowerShell CoreだけでなくWindows PowerShell(PowerShell 3.0以降)でも利用可能です。

インストール方法はいろいろありますがInstall-Moduleをするのが手っ取り早いでしょう。

Install-Module ThreadJob -Scope CurrentUser

先ほどの例もこの様にサクッと利用可能です。

f:id:stknohg:20180905174650p:plain

従来のジョブとThreadJobの使い分け

最後に従来のジョブとThreadJobの使い分けについて簡単に私見を述べたいと思います。

本エントリの例で示した様に単純な処理時間だけをみるとThreadJobだけ使えば良さそうに見えます。
性能は重要な要素ですのでThreadJobが使える環境では積極的にThreadJobを使うのが良いと思います。

ただし、従来のジョブにもメリットはあります。
個々のジョブが別プロセスであるため、仮にジョブ内部で予期しない例外が発生した場合でも呼び出し元に影響を及ぼすことは決してありません。
また、例えばメモリなどのリソースを大量に消費する処理をジョブにした場合、呼び出し元にそのリソース消費が影響することはありませんし、ジョブが終了されればプロセス自体が消えてしまうためリソースの解放漏れといったことを気にする必要が一切ありません。
ThreadJobではRunSpaceが分かれていても同一プロセスであるため、例外の種類によってはプロセス自体が落ちてしまう可能性は残りますし、リソースの消費や解放漏れについても気にかけてやる必要があります。

実行するジョブの内容に応じて従来のジョブのメリットを得ることができるのであればそれを利用するのが良いでしょう。

もしくは単純に

重たい処理には従来のジョブ、大量の並列処理にはThreadJob。

くらいの使い分けでも良いかと思います。