しばたテックブログ

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

PowerShell 6.0からジョブの実行に & が使える様になります

PowerShell 6.0 Beta.2からジョブの実行方法が拡張され、文の最後*1にアンパサンド(&)を付けることでジョブを起動することができる様になりました。

これはBash等のシェルにある機能と同等のものですが、PowerShellには従来からバックグラウンドで別プロセスのPowerShellを起動するジョブの仕組みがあり、&はその糖衣構文な感じで実装されました。

& によるジョブの起動例

簡単な例として

dir &

と実行するとジョブが起動され以下の様な画面になります。

f:id:stknohg:20171224104740p:plain

この記述は概ね

Start-Job -ScriptBlock {dir}

と一致し、その結果については従来どおりReceive-Jobなどで取り扱うことが可能です。

f:id:stknohg:20171224104758p:plain

& によるジョブ起動の詳細

さきほど 概ね一致 と書きましたが実際には幾つかの点が異なります。

ジョブ起動時のロケーション

&でジョブを起動したときは自動で以下のコマンドが挿入され現在のロケーションをジョブに引き渡す様になっています。

Microsoft.PowerShell.Management\Set-Location -LiteralPath $using:pwd ;

Start-Jobでジョブを起動した場合は何も挿入されません。

f:id:stknohg:20171224104823p:plain

変数の自動引き渡し

&でジョブを起動したときは自動で変数にusingスコープが付与され、その値がジョブに引き渡されます。

例として

$message1 = "Hello"
Write-Output $message1 &

を実行するとジョブで実行されるコマンドは

Microsoft.PowerShell.Management\Set-Location -LiteralPath $using:pwd ; Write-Output ${using:message1}

となり、message1変数にusingスコープが付与されていることが確認できます。
ジョブの結果を見てもちゃんとmessage1変数に値が渡されています。

f:id:stknohg:20171224104858p:plain

Start-Jobでジョブを起動する場合、usingスコープは自分で付与しなければなりません。
以下の様にジョブを実行してしまうとmessage2には何も設定されませんので注意が必要です。

$message2 = "World"
Start-Job -ScriptBlock { Write-Output $message2 }

f:id:stknohg:20171224105058p:plain

最後に

とりあえずこんな感じです。

これまでちょっと実行するのが面倒であったジョブの仕組みですが、&の登場によりより手軽にできる様になったと思います。

補足

本エントリはPowerShell Advent Calendar 2017 22日目の代理投稿になります。

*1:文法上正確にはパイプラインの最後になりますが、ここではわかりやすさのために文の最後と表記します

PowerShell 6.0で範囲演算子(..演算子)が拡張される話

とあるPull RequestがきっかけでPowerShell 6.0 RC1から範囲演算子(..演算子)が拡張され、Int型だけでなくChar型も扱える様になりました。

本エントリではその内容について説明します。

範囲演算子でChar型が扱える様になります

もともと範囲演算子(..)は、

1..5

の様に[Int型の数値]..[Int型の数値]の形式をとり、指定した数値間で連続する要素を持つ配列を生成します。

# 1~5を要素にもつ配列が生成される
1..5 => (1,2,3,4,5)

f:id:stknohg:20171221213103p:plain

今回、この指定がChar型を取れる様に拡張され、以下の様な記述が可能になります。

'a'..'e'

この例の場合だとaeを要素にもつ配列が生成されます。

# a~eを要素にもつ配列が生成される
'a'..'e' => ([char]'a', [char]'b', [char]'c', [char]'d', [char]'e')

f:id:stknohg:20171221213133p:plain

ただ、PowerShellにはChar型リテラルを表現する手段はないため、'a''e'の記述は実際にはString型であり内部的にChar型へのキャストが発生しています。

# 内部的にはString型→Char型へのキャストが発生しているイメージ
[char]'a'..[char]'e'

もう少し深入りしてみる

これだけだとなんとなくそんなものかという感じですが、この機能拡張はまだバギーなうえかなり気持ち悪い挙動をします。

PowerShellにおけるChar型と本機能拡張

元のPull Requestを見てわかる様に、この機能拡張はもともと、

'a'..'z'

の様な単純なASCIIの範囲の文字の使用を想定している様です。

ただ、PowerShellは.NET Framework/.NET Core製であり.NETの世界のChar型はUnicode Characterです。

このため、

'あ'..'お'

の様な指定も可能となり、以下の様な結果を返します。

f:id:stknohg:20171221213229p:plain

これはざっくり以下と同義になります。

[int][char]'あ'..[int][char]'お' | % { [char]$_ }

現時点で発生しているバグ

現時点で以下のバグが報告されており、

github.com

'あ'..'お' | % { [int]$_ }

の様な記述がエラーとなってしまいます。

f:id:stknohg:20171221213254p:plain

いまのところ()でくくることで回避可能ですが早急に修正してほしい感じです。

# ()でくくることでバグを回避できる
('あ'..'お') | % { [int]$_ }

また、私も別件で以下のIssueを報告しています。

github.com

現状の出来を見るに、個人的には機能を拡張するには軽率だったんじゃないかと思いますし、今からでもこの拡張は取り下げた方が良いんじゃないかとさえ思えます...

型変換の仕様との演算子の整合性について

個人的な感情はとりあえず置いておいて、今回の拡張によって範囲演算子はInt型およびChar型を受け入れることができる様になりました。

ではInt型とChar型を混在させたときはどうなるかというと次の様になります。

  • [Char型] .. [Int型]の場合はOK
# OK
'a'..100

f:id:stknohg:20171221213537p:plain

  • [Int型] .. [Char型]の場合はNG
# NG
95..'a'

f:id:stknohg:20171221213552p:plain

これはPowerShellの二項演算子が左オペランドの型に合わせようとする原則があるためです。
比較演算子のケースですが過去のこちらのエントリが参考になるでしょう。

blog.shibata.tech

PowerShellではInt型からChar型へのキャストは直接行えるのですが、

# OK
[char]100

Char型からInt型へのキャストについては、記述上String型からInt型へのキャストが必要になり、PowerShellにはString型を直接Intにキャストできないため構文エラーとなります。

# NG : String型 → Int型へ直接キャストはできない
[int]'a'

# OK : Char型からInt型へのキャストなら可
[int][char]'a'

なお、Char型からInt型へのキャストは可能なので、

# OK : String型 → Char型 → Int型へのキャストとなる
95..[char]'a'

の様に記述すれば[Int型] .. [Char型]の場合でもエラーなく実行することができます。

f:id:stknohg:20171221213804p:plain

【2017/12/22】 ちょっと追記

String型からInt型へのキャストについて、

[int]'1'

の様に文字列で数値を表現している場合は可能です。
このため、

# これはPowerShell 6.0以前でも可能
'1'..'5'

# これもPowerShell 6.0以前で可能
95..'97'

の様な記述であればエラーなく可能です。

最後に

とりあえずこんな感じです。

この範囲演算子の拡張は個人的には不要とさえ思っているのですが何かしら役に立つケースもあるかもしれません。

PowerShellGetをアップデートしよう

PowerShell Advent Calendar 2017、18日目です。

qiita.com

PowerShell 5.0以降、Windows 10やWindows Server 2016では標準でインストール済みのPowerShellGetの機能について、現時点での最新バージョンはVer.1.6.0なのですが、例えばWindows 10においてはFall Creator Update(1709)を適用してもVer.1.0.0.1と古いバージョンのまま更新されません。

www.powershellgallery.com

PowerShellGetの変更履歴はこちらあり、これまでいくつかのバグフィックスや機能追加がなされています。
例えば最新版のVer.1.6.0ではプレリリースバージョンのモジュールのインストールに対応する様になっています。

一般論としてできるだけ新しいバージョンのモジュールを利用していくのが良いため、本エントリではその手順を紹介していきます。

PowerShellGetをアップデートする

本エントリではWindows 10 Fall Creators Update(1709)を検証環境として説明します。

はじめに更新前のバージョンをチェックしてみます。
PowerShellGetは内部でPackageManagementモジュールを使用するのでこちらのバージョンも確認しておきます。

Get-Module PowerShellGet,PackageManagement -ListAvailable

f:id:stknohg:20171218001908p:plain

どちらのモジュールもVer.1.0.0.1と古いままです。

通常、モジュールのアップデートにはUpdate-Moduleコマンドレットを使いますが、何も考えずに実行すると以下のエラーメッセージが出てしまいます。

Update-Module PowerShellGet 

f:id:stknohg:20171218002004p:plain

このためInstall-Moduleコマンドレットを使い新しいバージョンを別にインストールする体をとる必要があります。
Install-Moduleコマンドレットですが、インストールするモジュールを過去バージョンと併用するには-Forceオプションが必要です。
また既存コマンドの動作を上書き許可するため-AllowClobberオプションも必要になります。

Install-Module PowerShellGet -Scope CurrentUser -Force -AllowClobber 

インストール後はこんな感じになります。

f:id:stknohg:20171218002024p:plain

  • PowerShellGet : Ver.1.6.0
  • PackageManagement : Ver.1.1.7

と、両モジュールとも新しいバージョンが追加インストールされていることが分かります。

コンソールを再起動すると新しいバージョンのモジュールが読み込まれ利用可能になります。

PowerShellGetの新機能を試してみる

最初に新しいバージョンのPowerShellGetでは

プレリリースバージョンのモジュールのインストールに対応する

と説明しました。

ちょうど今PowerShellコンソールに様々な拡張機能を提供しているPSReadlineモジュールがVer.2.0 ベータをプレリリース版として公開しているので試しにインストールしてみます。

PSReadline 2.0は1.x系に対して主にPowerShell Coreへの対応とバグフィックスが主な変更点ですが、

  • 日本語入力時にキャレット位置が狂うバグの修正(#542)
  • 行頭でバックスペースキーを押したときにBeep音がならない様に修正 (#422)

などの日本語ユーザーにとっても嬉しい修正が入っているので積極的に試してみると良いと思います。

PSReadline 2.0 Betaをインストールしてみる

プレリリース版のモジュールを取り扱うには新たに追加された-AllowPrereleaseオプションを指定してやります。

モジュールを検索する場合にはFind-Module-AllowPrereleaseオプションを指定します。

Find-Module PSReadline -AllowPrerelease 

f:id:stknohg:20171218002156p:plain

-AllowPrereleaseを付けたときだけプレリリース版であるVer.2.0.0-beta1がヒットするのが見て取れます。

続けて、プレリリース版のモジュールをインストールするにはInstall-Module-AllowPrereleaseオプションを指定してやります。
過去バージョンのモジュールと併用するには-Forceオプションが必要です。
また、既存のバージョン(ここではVer.1.2)が署名済みのため、Ver.2.0でも署名を求められており、いまのところは-SkipPublisherCheckを付けてやる必要があります。

Install-Module PSReadline -AllowPrerelease -Scope CurrentUser -Force -SkipPublisherCheck

f:id:stknohg:20171218002355p:plain

エラー無く処理が終わればインストール完了です。
コンソールを再起動すると新しいバージョンのモジュールが読み込まれます。

補足 システム全体へのインストール

ここまでの手順では各コマンドを-Scope CurrentUserで実行してユーザー毎のインストールを行いましたが、もちろん管理者としてシステムワイドにインストールしても構いません。

この場合は-AllowClobberオプションは不要になります。

Install-Module PowerShellGet -Force

f:id:stknohg:20171218002432p:plain

コンソールを再起動すると新しいバージョンのモジュールが読み込まれます。

同様にPSReadlineも管理者としてインストール可能です。

Install-Module PSReadline -AllowPrerelease -Force -SkipPublisherCheck

f:id:stknohg:20171218002452p:plain