2017/12/01

この記事はPowerShell Advent Calendar 2017の1日目です。

毎年恒例のPowerShell Advent Calendar、今年も始まりました。ここ数年は私がトップバッターを務めさせていただいて、1年間のPowerShell界隈の出来事をさくっとまとめてみています。→2016年2015年

昨年2016年はPowerShell 10周年の年であり、PowerShell 5.1、Windows Server 2016、Nano ServerとPowerShell Core Editionが各々正式版としてリリースされ、さらにはPowerShellがオープンソース化、マルチプラットフォーム展開を始めるという大きな変革があった年でした。

今年2017年は昨年ほど大きな変化はないとはいえ、昨年のOSS化からのマルチプラットフォーム展開を着実に進行させた年だと言えると思います。

以下、いくつかトピックを紹介します。

WMF 5.1インストーラーの登場

PowerShell 5.1を含むWMF (Windows Management Framework) 5.1は、Windows2016に同梱され、昨年8月にリリースされたWindows 10 Anniversary Updateにも同梱されました。今年1月に公開されたWMF5.1のインストーラーは、下位OS(Windows 7/8.1/Server 2008 R2/2012/2012 R2)のためのものです。

なお、Win10/2016に同梱のPester(テストフレームワーク)やPSReadline(コンソール入力支援)についてはWMF5.1には含まれていないので、別途PowerShellGetでインストールするのがお勧めです。

Azure Cloud ShellでのPowerShell サポート

Webブラウザ上で動作するAzureの管理用シェルである、Azure Cloud ShellではまずBashがサポートされていましたが、今年9月にPowerShellもサポート(まだプレビューですが)されました。

自動的に認証された状態で最新のAzure PowerShellのコマンドが使え、AzureのリソースにAzure:ドライブを介してアクセスすることが可能です。

注意点があって、このPowerShell版Azure Cloud Shell、どうも現バージョンでは(Nano Serverではなく)Windows Server Coreのコンテナ上で動作しているらしく、Bashに比べ起動が若干遅いのと、実体はPowerShell CoreではなくWindows PowerShell 5.1であることはちょっと念頭においておいたほうがいいかもしれません。

これ、PowerShell CoreではまだAzure PowerShellの全機能がサポートされてないからだと思うんですが、今後に期待ですね。

PowerShell for Visual Studio Code 正式版リリース

先だってオープンソース化された、マルチプラットフォーム対応のコードエディタであるVisual Studio CodeでPowerShellスクリプトの開発を行うためのExtension、PowerShell for Visual Studio Codeの正式版(1.0)が5月に公開されました。なお、現時点での最新バージョンは1.5.1となっています。

当初は、PowerShellに付属の標準スクリプト開発環境、PowerShell ISEの方が多機能だったようにも思いますが、今はもう完全にISEの機能を追い越したんじゃないかと思います。シンタックスハイライト、インテリセンス、デバッグ、コンソールといった基本機能はもちろん、Gitによるバージョン管理もVSCode自体でサポートされていることに加え、静的解析機能を提供するPowerShell Script Analyzer、テストフレームワークのPester、プロジェクト管理機能を提供するPlasterなどが統合されており、本格的な開発環境となっています。

また当然ではありますが、マルチプラットフォーム対応なので、WindowsではWindows PowerShell 、LinuxやMacではPowerShell Coreの開発が各々可能です。

公式ブログでのアナウンスによれば、今後ISEがなくなることはありませんが、ISEに新機能が追加されることはなくなり、PowerShell for VSCodeの開発に注力されることになります。ISEはとにかく標準添付である(GUI有効ならサーバーOSでも動く!)という強みがあり、シンプルなスクリプト記述であればそこそこ便利に使えるので、これからもシチュエーションに応じて使い分けて行けば良いのかなと思います。

PowerShell Core RCのリリース

昨年OSS化したPowerShell Core 6はα版として開発が続いていましたが、今年5月にはβ版となり、先月(11月)、ついにRC(Release Candidate)となりました。6.0.0のGAリリースは来年1月になるそうです。

OSS化直後からRCに至るまでの変更点は多岐に渡り、とても一言で説明できるものではないですが、ポイントとしては以下の3点に集約されるんじゃないかと思います。

  1. PowerShellが長年抱えていた問題点の洗い出しと修正

    PowerShellがOSS化した当初は、ほとんどがWindows PowerShell 5.1のコードそのままであったと言ってよいかと思います。10年以上増改築が繰り返されたコードが突如、全世界に公開されたわけです。コミュニティの力でバグや変な仕様といった問題点が洗い出され、どんどん修正されていきました。
    また、不足していると思われる機能はどんどん追加されました。既存コマンドレットのパラメータが増えるというパターンが多かったように思います。

    特筆すべきは、破壊的変更であっても妥当性があれば躊躇せずに取り入れていったことかと思います。これは英断ではありますが、一方でWindows PowerShell 5.1とPowerShell Coreでは細かいところで非互換性が色々出ていますので、移行の際には注意を要します。

  2. マルチプラットフォーム対応

    前述の通り、OSS化した当初のPowerShell 6.0は、ほぼWindows PowerShell 5.1なので、Windowsでしか動作しない部分が多々ありました。それをLinuxやMac環境でも動作するように多くの修正が加えられました。

    ところで、PowerShell 6.0は当初、条件付きコンパイルにより、Windows用に.NET Framework(Full CLR)をターゲットにして、Desktop Edition相当のPowerShellをビルドすることが可能でした。

    しかしβ版になったタイミングで、OSS版PowerShell 6.0は、「PowerShell Core 6.0」すなわち、「.NET Core上で動作するPowerShell Core」であることが明確にされました。よってFull CLRターゲットのビルドはできなくなり、β6ではついにFull CLR対応のコードはすべて削除され、Core CLR対応のコードのみとなりました。

  3. Windows PowerShell用コマンドレットの呼び出し

    PowerShell Core 6.0にはいくつかのコマンドレットが同梱されていますが、Windows PowerShell 5.1に含まれているすべてのコマンドレットを網羅しているわけではありません。また、WindowsやWindows Serverの管理のために提供されている、OS付属のモジュール群もCore 6.0には含まれておらず、α版の段階では実行も不可能(だったはず)でした。

    β1からターゲットが.NET Core 2.0に移行したことにより、.NET Standard 2.0がサポートされました。このことによって、Windowsに付属の数千ものコマンドレットを初めとするWindows PowerShell用コマンドレット(要はFull CLRをターゲットとしてビルドされたもの)のうち、.NET Standard 2.0に含まれるAPIしか使われていないものであれば、原理的にはPowerShell Coreでも実行可能になりました。

Windows PowerShellの今後

さて、PowerShell Core 6.0がまもなく正式版リリースということですが、では従来のWindows PowerShellはどうなるのか、という話について。

公式ブログのアナウンスによれば、Windows 10やWindows Server 2016に付属のWindows PowerShell 5.1については、今後もサポートライフサイクルに則り、重大なバグフィックスやセキュリティパッチ提供等のサポートは継続されます。もちろん下位バージョンのOS/Windows PowerShellも同様です。

しかしながら、Windows PowerShellに新機能が追加されることは今後はなく、開発のメインはPowerShell Coreへと移行します。つまりは、PowerShell Coreの開発の中で追加された新機能、変更点、バグフィックスについては、基本的にはWindows PowerShellとは無関係ということです。

また、現状ではPowerShell CoreはWindows PowerShell環境に追加インストールし、サイドバイサイド実行が可能となっていますが、将来的にPowerShell CoreがWindowsに同梱されるかどうかについては言及されておらず、今のところは不明です。

以下は私見になります。

このような状況で、Windows PowerShellユーザー、とりわけWindows Serverの管理はするが、Linuxとかは特に…というユーザーはこれからどうすべきか?という点は割と悩ましいところだと思います。個人的には、WindowsやWindows Serverを管理するスクリプトが現時点であるなら、それを無理に今すぐCore対応にする必要はないと思います。現時点で今すぐCoreに移行すべき理由というのはとくに無いと感じます。Coreで追加、改善された機能はあるものの、Coreには無い機能もたくさんあるからです。
また新規にスクリプトを作る場合でも、対象がWindowsに限定されるのであれば、Windows PowerShell用に作れば良いのではないかと。OS付属のコマンドレットの動作は確実に保証されているわけですから。

ただし、ご存じの通りWindows10とServer 2016は半期に一度の大型アップデートで新機能が次々追加されていきます。その過程でPowerShell Coreが含まれるようになったり、Coreのみ対応のコマンドレットが追加される可能性は無きにしもあらずなのではないかとも思います。なので、Coreの状況をチラ見しつつ、未来に備えておく必要はあると思います。Windows10/Server2016の「次」も見据えて。

それとPowerShellでWindowsもLinuxも面倒みていきたい、という野心がある方は、Coreを採用していくのがいいのではないかと思います。ただし、現状しばらくは茨の道ではあるとは思います。

あとはスクリプトやモジュールを作成し公開する方は、より多くの環境で使われるように、可能であればCore対応を進めるのは悪くないんじゃないかと思います。

おわりに

他にもWin10/Server2016におけるPowerShell 2.0の非推奨化の話とか、DSC Core構想とか、なにげに結構いろいろ話題はありました。

さて、Windows PowerShellとしては一端落ち着いた感もある界隈ですが、PowerShell Coreとしてはこれからも活発に動いていくものと思います。注目していきたいですね。

そんな2017年の締めくくり、今年はどんな記事が集まるでしょうか。PowerShell Advent Calendar 2017の参加、お待ちしております。

2016/12/01

この記事はPowerShell Advent Calendar 2016の1日目です。

PowerShellアドベントカレンダー、今年も始まりました。みなさまのご協力の甲斐あって、過去5年間はすべて完走していますが、今回もできれば完走を目指していく感じでまいりましょう。色々な立場の方からの視点でPowerShellを俯瞰できるこのイベント、私自身も毎年楽しんでおります。

さて、去年も2015年のPowerShellをまとめる的な記事から始めたわけですが、今年も去年に負けず劣らず、大変革の年だったと思いますので、今回も1年を振り返るところからスタートしましょう。

Bash on Ubuntu on Windows の登場

去年のPowerShell5.0登場、周辺モジュールのOSS化といった大きな動きがあってから、今年の前半は少しおとなしめ?の界隈でした(5.0のインストーラーにバグがあって一時非公開とか小騒動はありました)が、まず驚いたのがPowerShellそのものではなくて、Windowsで動くbashが登場したというトピックですね。発表があったのは今年の3月末の事です。

bashとは言わずと知れた、Linuxの標準シェルですが、これをWindows 10の"Windows Subsystem for Linux"という仕組みの上で動作するUbuntu上で動作するbashとして動作させてしまおうというものです。なので正式には"Bash on Ubuntu on Windows"という名称になります。

このbash、Windowsのシステムを管理するためのものではなく、Web等の開発用途を想定して提供されたものです。なので本来的にはPowerShellとは関係ないのですが、当初は色々と誤解が飛び交ったように思います。曰く、MSはPowerShellを捨ててやっとbashを採用した。PowerShellとは何だったのか。等々…。

これらの誤解や疑問には、PowerShellの公式ブログで、bashという新たなシェルがWindowsに追加されたが、両者は並立するものだ、PowerShellはこれからも進化するよ!といった異例の公式見解が示されたりもしました。

Bash on Ubuntu on Windowsは、後述するPowerShell 5.1とともに、8月リリースのWindows 10 Anniversary Updateで正式に利用可能となりました。

PowerShell 5.1 の登場

今年7月には、PowerShell 5.1 / WMF (Windows Management Framework 5.1)のプレビュー版が登場しました。[リリースノート]

そもそもPowerShell 5.0はWindows Server 2016のためのシェルとして開発が進められていたはずですが、先にWindows 10に同梱されたものの、2016正式版までやや時間を要すこととなりました。その間にPowerShell 5.0にはいくつかの機能や改良が加えられ、結局、5.1というバージョンが登場するに至ったものと思われます。

PowerShell 5.1は、前述の下位OS用のプレビュー版の他、今年8月初めのWindows 10 Anniversary Updateという大型アップデート適用で使えるようになりました。そしてその後、今年10月に正式版が登場したWindows Server 2016にも(もちろん)同梱されました。

5.1ではローカルユーザーやグループを扱うコマンドレット等が(ようやく?)追加されたりもしています。が、一番大きな変更点は、Windows Server 2016に追加された新機能である、Nano Server用のPowerShellが、従来のPowerShellと分離した点でしょう。

従来の、.NET Frameworkで動くフル機能のPowerShellは、5.1からは"Desktop Edition"と呼ばれます。対して、コンテナに最適化させるため、フットプリントを最小にしたNano Server(や、Windows IoT等)で動作するコンパクトなサブセット、"Core Edition"が新たに登場しました。

Core Editionは、.NET Frameworkのコア部分を実装した、.NET Core上で動作します。ちなみに.NET CoreはOSS(オープンソースソフトウェア)となっています。

Core Editionはサブセット版というだけあって、今となっては若干レガシーにもなった一部のコマンドレットが使えないことを初め、いくつかの制限事項もありますが、概ね、Nano Serverの管理に必要十分な機能を保っているのではないかと個人的には思っています。

PowerShell オープンソース化

今年、PowerShell界をもっとも震撼させたニュース、それは間違いなく、今年8月に実施された、PowerShellのオープンソース化でしょう[GitHub]。周辺モジュールのOSS化など、これまでの流れからいくと、確かに本体OSS化の機運は高まっているように個人的にも感じていましたが、OSS化するとしてもCore Edition部分止まりだろうなーと思っていたら、まさかのDesktop Editionを含んだ全体だったので驚きました。

そしてOSS化の副産物(と個人的には感じる)である、PowerShell on Linux、PowerShell on Macが登場しました。これも一部の方、特にWindowsやMicrosoft製品を普段あまり使われない方に、割と大きなインパクトを与えていたように思います。

PowerShellがオープンソースになったこと、"PowerShell for every system!"になったことの意義についてはちょっと語りつくすには時間が足り無さそうですが、敢えて現実ベースの話を先にすると、一般ユーザー(PowerShellをシステム管理に用いている管理者)にとって、すぐに世界が変化するかというとそうではないんじゃないかという気がしています。

というのも、現在のところOSS版のPowerShellのバージョンは「6.0」とされているものの、まだα版の段階で、基本機能はほぼほぼ5.1と変わらないです。OSS版が改良されても、別に今Windowsで使っているPowerShellがすぐに強くなるわけではありません(現在のところ、サイドバイサイドでインストールする)。オープンソース、マルチプラットフォーム展開を始めたといっても、それは現在の所、PowerShell本体とコアモジュールに留まっていて、コマンド数もたかだか数百個程度でしょう(もうちょっとあるかな?)。PowerShellでOS、サーバー、アプリ、インフラを管理するには、専用のコマンドが山ほど必要になってきますが、それらは今まで通り、Windowsの製品にしか含まれないものです。そのような状況で、たとえばLinuxを管理するのには自分でコマンドを作るか、普通にLinuxのコマンドを呼ぶか…あれそれって別に普通のシェルスクリプトでいいんじゃ…とか。

今後は、OSS側での成果が、Windows / Windows Server上のPowerShellに反映され、両者は一本化される、はず、です、たぶん、が、それはまだアナウンスもなく、いつ、どのような形でもたらされるかは不明と云わざるを得ません。

もちろん、この状況はあくまで現時点の状況です。ゆくゆくはPowerShellで、WindowsもLinuxもMacも一貫したコマンド&スクリプトで管理できる日が来るかもしれませんね。現在のところは、PowerShellの謎挙動に遭遇したとき、ソースを合法的に眺めて思いを馳せることができるようになったのが大きいかと個人的には思います。もちろん腕に覚えのある方は、(ルールに従って)どしどしプルリクエストを投げて、PowerShellを自ら育てていただければな、と思います。

PowerShell 10周年

とまぁ、激動の1年が終わりかけた先日の11/14には、PowerShell 1.0が登場してちょうど10年ということで、PowerShell10周年イベントがあったりしました。思えば遠くへ来たものですね。

さてさて、PowerShellを取り巻く状況は刻一刻と変化し、去年と今年ではその傾向が顕著です。おそらく節目の年である今年の最後を飾ることになる、PowerShell Advent Calendar 2016を、どうぞよろしくお願い致します。

2016/07/11

大変遅くなってすみません。7/2(土)、札幌で開催されたCLR/H #clrh101で行ったセッション、「PowerShell の概要と 5.x 新機能のご紹介」の資料を公開します。

札幌は涼しくて、何もかも美味しくて良かったです。夏の関西は人間の生存に適した気候とはとても言えないので、しばらく札幌に滞在していたかったですね。

さて、登録ページでのタイトルと若干違いますが、間もなく(8/2に)Windows 10 Anniversary Updateの登場とともにPowerShell 5.1の正式版が利用できるようになるということで、今回、5.0に加えて5.1の新機能もご紹介することにしたためです。(スライドは5.1の新機能の部分以外は、基本的にこれまでの内容と同様です。ご容赦ください。)

なお、PowerShell 5.1は現在のところ、Windows 10 Insider PreviewかWindows Server 2016 TP5で試すことができます。

PowerShell 5.0の登場からWindows Server 2016正式リリースまでの期間がけっこう空いたことと、ラピッドリリースの方針もあって、5.1が短期間で登場することとなりました。なのでどれが5.0でどれが5.1の機能かというのは割と曖昧ですけど、5.1で一番大きく変化するところは、PowerShellのエディションがDesktop EditionとCore Editionに分かれるところだと思います。

PowerShell Core Editionは従来のPowerShellのサブセット版となり、Windows Server 2016のインストールオプションの一つで、フットプリントを軽量化し、Windowsコンテナ技術に最適化された、Nano Serverで動作させることを目的として作られました。

Core Editionでは一部のコマンドレットのみのサポートとなりますが、基本的にNano Serverの管理はリモート経由でPowerShellを直接的あるいは間接的に用いて行うことになるため、当然ながら必須のコンポーネントとなります。

なお、PowerShell Core Editionは先日、1.0リリースを迎えたばかりの、.NET Core上で動作します。.NET Coreとは.NET Frameworkのサブセットで、マルチプラットフォームで動作し、OSSとして開発されています。

.NET Framework上で動作する、従来のフルセット版PowerShellも、Desktop Editionとしてこれまで通り利用可能です。

PowerShell 5.0、5.1の新機能はMSDNでまとめられているのでそちらもご参照ください。

Windows 管理フレームワーク (WMF) 5.0 RTM のリリース ノート概要 | MSDN
WMF 5.1 Release Notes (Preview)

追伸。Microsoft MVP for Cloud and Datacenter Managementを7月付で再受賞いたしました。おかげで今回のイベントで「関西MVP3人が集結」という触れ込みが嘘にならなくて良かったです。そして同じ分野でCLR/Hのスタッフでもある素敵なおひげさんも受賞されました。おめでとうございます。

2012/11/21

先日、Windows Server 2012がRTMされたのと同時に、Windows 7/2008/2008 R2用のPowerShell 3.0を含むWindows Management Framework (WMF) 3.0パッケージも公開になりました。

Download WMF 3.0 from Official Microsoft Download Center

また、Windows Server 2012を管理するためのWindows 8用リモートサーバー管理ツール(RSAT)も公開されました。サーバーマネージャーやAD管理センターなどの管理ツール、Windows Server 2012の「役割」と「機能」に対応するPSモジュールが含まれています。

Download: Windows 8 用 RSAT - Microsoft Download Center - Download Details

(残念ながらWin Server 2012を管理するWin7用のRSATはないようです)

さて、PowerShell 3.0はローカルヘルプが標準では含まれていないので、Update-Helpコマンドレットを管理者権限で使ってダウンロードする必要があります。が、現状では日本語ヘルプが存在しないので、ローカルでヘルプが引けないというちょっとアレな状況になってます。

この状況、いつかは改善されるとは思うのですが、とりあえずの回避策を書いておきます。以下のスクリプトを管理者権限で実行してください。

Update-Help -UICulture en-us -Force
mkdir $pshome\ja-JP_backup
copy $pshome\ja-JP\*.* $pshome\ja-JP_backup\
copy $pshome\en-US\*.* $pshome\ja-JP\

このスクリプトはUpdate-Helpコマンドレットにより英語ヘルプ(ロケールen-us)を$pshome\en-USにダウンロードします。その後、ダウンロードしたヘルプファイルをそのままja-JPフォルダにコピーします。その際、念のためにバックアップをja-JP_backupフォルダにとっておきます。

これで日本語環境のpowershell.exeでもとりあえずは英語版のヘルプを引くことが出来るようになります。

(2013/01/18追記。Windows 8とServer 2012では、Update-Help -UICulture en-us -Forceを管理者権限で実行するだけで、日本語環境でも英語ヘルプが表示されます。)

日本語ヘルプがダウンロード可能になればこの作業は必要ないですが、それまでの暫定措置としてご利用ください。

この作業に抵抗がある方は、オンライン版英語ヘルプを参照するのでもいいかと思います。

Windows PowerShell Core Modules
これはPowerShell 3.0に標準添付のモジュールとそれに含まれるコマンドレットのリファレンスです。

Windows PowerShell Support for Windows Server 2012
これはWindows Server 2012 / Windows 8に付属のモジュールとそれに含まれるコマンドレットのリファレンスです。

powershell.exeで Get-Help コマンドレット名 -Online とすることでWebブラウザを開いてこれらのヘルプページを直接表示することも可能です。

さて、ヘルプが揃ったところで、さっそく新しいコマンドレットを試してみましょう。そしてその成果をぜひ、PowerShell Advent Calendar 2012で発表してください!!

2011/12/25

はじめに

PowerShell Advent Calendar 2011の25日目最終日の記事、そしてこれが私の記事では4回目となります。今回もバックグラウンドジョブについての話題です。今回はバックグラウンドジョブを使って並列処理をやってみようという試みです。

これまでの記事は以下になります。

2日目:バックグラウンドジョブの使い方・基本編

13日目:バックグラウンドジョブとの通信

19日目:PowerShell 3.0で追加されるバックグラウンドジョブ関係の新機能

ところでつい2日前、WMF3 CTP2 Windows PowerShell Workflow.pdfというpdfファイルが公開されました。これは19日目に書いたPS workflowについての詳しい説明(英語)です。構文だけでなくPSスクリプトとの違いやWFとの関係などが詳しく書かれています。ぜひ目を通しておくことをお勧めします。23日目のAhfさんの記事と併せて読むと理解が深まると思いますよ!

並列処理スクリプト

C#をご存知の方なら、PowerShellのバックグラウンドジョブ機能はC#4.0から使えるTaskオブジェクトとちょっと似てるかなーと思われるかもしれません。ではC#4.0でコレクションに対して並列処理でループを回すParallel.For()やParallel.Invoke()みたいなことはPowerShellでできないのか、という疑問が出てくるかと思います。

前回述べたようにPowerShell 3.0ならworkflowを使えば並列処理が可能で、for -parallelステートメントやparallelブロックでParallel.For()やParallel.Invoke()みたいなことが可能になります。しかしPowerShell 3.0がリリースされるのはまだ先ですし制限事項も多いので、なんとかPowerShell 2.0で、しかもworkflowのような制限なしで、並列処理のスクリプトは書けないものかと考えてみました。

function ParallelForEach-Object
{    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,Position=1)][scriptblock]$process,
        [scriptblock]$begin={},
        [scriptblock]$end={},
        [Parameter(ValueFromPipeline=$true)][psobject]$inputObject
    )
    begin
    {
        &$begin
        $jobs=@()
    }
    
    process
    {    
        $jobs|Receive-Job
        while(@($jobs|?{$_.State -eq "Running"}).Length -ge 5)
        {
            $jobs|Receive-Job
            start-sleep -Milliseconds 100
        }       
        
        $jobs += Start-Job $process -argumentList $inputObject
    }

    end
    {
        while(@($jobs |?{$_.State -eq "Running"}).Length -gt 0)
        {
            $jobs|Receive-Job
            start-sleep -Milliseconds 100
        }
        $jobs|Receive-Job
        $jobs|remove-job
        &$end
    }
}




$watch=new-object System.Diagnostics.Stopwatch

"ForEach-Object 開始"

$watch.Start()
1..10|ForEach-Object {
    "start: " + $_
    Start-Sleep -sec 5
    "end: " + $_
    
}
$watch.Stop()

"ForEach-Objectの場合:" + $watch.Elapsed.TotalSeconds + " sec"

$watch.Reset()

"ParallelForEach-Object 開始"

$watch.Start()
1..10|ParallelForEach-Object {
    "start: " + $args[0]
    Start-Sleep -sec 5
    "end: " + $args[0]
}
$watch.Stop()

"ParallelForEach-Objectの場合:" + $watch.Elapsed.TotalSeconds + " sec"

ParallelForEach-Object関数はパイプラインから渡されたコレクションの各要素について、並列にスクリプトブロックを実行させるものです。同等の処理をForEach-Objectを使って同期的に逐次処理した場合とかかる時間を比較しています。10個の要素があり、各要素につき5秒かかる処理なので、逐次的に処理すると当然50秒以上かかりますが、ParallelForEach-Object関数を使って並列処理させると環境にもよりますが20秒以内に完了します。

この関数では渡されたコレクション1要素に対し1つのジョブを割り当て、同時に5ジョブまで(呼び出し元を含めて同時稼働が6プロセスまで)を並列実行するようにしています。

ただこれはあくまでなんちゃって並列処理なので、並列化することで本当に処理が高速になるかどうかは環境次第かと思います。一応、うちのCore2Duo (2コアCPU)な環境だと、足し算を3万回ほどする処理を10回行う場合、逐次処理とこの関数を使った並列処理では54秒が39秒に短縮され、有意な実行時間差が出ました。

またジョブを開始するのに新しくプロセスを起動させるので、1ループあたりの実行時間がプロセス起動にかかる時間より短ければ、この関数による並列化で処理時間の短縮は見込めません。

処理の対象が複数のリモートPCである場合などは割と有効なのかなと思います。たとえば複数サーバーから別々のファイルを同時にダウンロードするときなど。

ここではParallel.For()やParallel.ForEach()相当の関数を書きましたが、Parallel.Invoke()のような関数も書けるかと思います。スクリプトブロックの配列をStart-Jobで順に走らせ、Wait-Job, Receive-Jobする感じですね。

あとここではやりませんでしたが、Start-Jobの代わりにInvoke-Commandを使い複数のリモートPCに処理を振り分ければ、なんちゃって分散処理もできるのかなあと思いました。

おわりに

実はこのスクリプトを書いたのはPS Workflowの調査前のことで、Workflowで同様のことが可能になることを知って少々愕然としたのですが、それなりに面白いスクリプトかと思ったので公開することにしました。ともあれ、これからのマルチコア、メニーコアの時代、非同期処理や並列処理はますます重要になるかと思います。管理スクリプトにおいてもこれらの概念を意識しないわけにはいかなくなるでしょう。全4回にわたってPowerShellのバックグラウンド機能を解説してきましたが、これらがあなたの非同期&並列スクリプトライフ(?)の一助になれば幸いです。

さてさて、これでPSアドベントカレンダー2011もおしまいです。楽しんでいただけたでしょうか? 私自身も自分で記事を書いていて楽しかったですし、他の方の記事を読むのも色々な発見があり、とても有意義な25日間でした。記事を書いて参加していただいた方々、そして読者の方々に厚く御礼申し上げます。これからもぜひ、PowerShellを活用し、楽しんでくださいませ。

それでは皆様、良いクリスマスをお過ごしください!

2010/09/20

文字列を連結する際、+=演算子などを使うとループ回数によっては非常に時間がかかることがあります。これは+=演算子が実行されるたびに毎回string型の新しいインスタンスを生成しているからです。同じ文字列変数に対して+=演算子で文字列を追加していくループがある場合は、+=演算子の代わりにStringBuilderクラスを使うのが良いです。

たとえば、

$str=""
@("aaaa","bbbb","cccc","dddd")|
%{$str += $_}
$str

というようなコードと同様の結果を得るためには、

$sb=New-Object System.Text.StringBuilder
@("aaaa","bbbb","cccc","dddd")|
%{[void]$sb.Append($_)}
$sb.ToString()

のようにします。[void]にキャストしているのは、AppendメソッドがStringBuilderのインスタンスを返すため、それを表示させないようにするためです。結果はいずれも

aaaabbbbccccdddd

となり、文字列の連結が可能です。

このようにループ回数が少ない場合はそれほど所要時間に差はないのですが、数千の文字列を連結していく場合だと+=演算子を使うと非常に時間がかかります。どれくらい差が出るのか、スクリプトを書いて検証してみました。

function Measure-StringJoinCommand
{
    param([int]$itemCount)
    
    $randomStrs=@()
    @(1..$itemCount)|%{$randomStrs += Get-Random}
    $StringJoinTime=@()
    $StringBuilderTime=@()
    @(1..3)|
    %{
        $StringJoinTime +=
            (Measure-Command {
                $str=""
                $randomStrs|%{$str+=$_}
                $str
            }).Ticks
        $StringBuilderTime +=
            (Measure-Command {
                $sb=New-Object System.Text.StringBuilder
                $randomStrs|%{[void]$sb.Append($_)}
                $sb.ToString()
            }).Ticks
    }   
   
    $result=New-Object psobject
    $result|Add-Member -MemberType noteproperty -Name ElementCount -Value $itemCount
    $result|Add-Member -MemberType noteproperty -Name StringJoinTime -Value (($StringJoinTime|Measure-Object -Average).Average/10000)
    $result|Add-Member -MemberType noteproperty -Name StringBuilderTime -Value (($StringBuilderTime|Measure-Object -Average).Average/10000)
    $result|Add-Member -MemberType noteproperty -Name StringJoinTicksPerElement -Value (($StringJoinTime|Measure-Object -Average).Average/$itemCount)
    $result|Add-Member -MemberType noteproperty -Name StringBuilderTicksPerElement -Value (($StringBuilderTime|Measure-Object -Average).Average/$itemCount)
    $result 
}

@(100,300,500,800,1000,3000,5000,8000,10000,30000,50000,80000)|
    %{Measure-StringJoinCommand -itemCount $_}|
    Format-Table -Property `
        @{Label="Elements";Expression={$_.ElementCount.ToString("#,##0")};Width=8},
        @{Label="StringJoin(msec)";Expression={$_.StringJoinTime.ToString("#,##0")};Width=20},
        @{Label="StringBuilder(msec)";Expression={$_.StringBuilderTime.ToString("#,##0")};Width=20},
        @{Label="StringJoin(tick/element)";Expression={$_.StringJoinTicksPerElement.ToString("#,##0")};Width=30},
        @{Label="StringBuilder(tick/element)";Expression={$_.StringBuilderTicksPerElement.ToString("#,##0")};Width=30}

CPU=Intel(R) Core(TM)2 CPU 6600 @ 2.40GHz,memory=3GB,Windows 7 x86での実行結果は次の通り

Elements StringJoin(msec)     StringBuilder(msec)  StringJoin(tick/element)       StringBuilder(tick/element)   
-------- ----------------     -------------------  ------------------------       ---------------------------   
100      8                    6                    830                            646                           
300      22                   22                   719                            746                           
500      36                   34                   718                            689                           
800      60                   55                   755                            690                           
1,000    86                   72                   857                            721                           
3,000    264                  192                  880                            638                           
5,000    573                  334                  1,146                          667                           
8,000    1,534                522                  1,917                          653                           
10,000   2,562                731                  2,562                          731                           
30,000   23,386               2,204                7,795                          735                           
50,000   60,805               3,730                12,161                         746                           
80,000   184,407              6,541                23,051                         818   

この表は左から、連結する文字列要素数(ループ回数)、+=演算子を使った場合の所要時間(ミリ秒)、StringBuilderを使った場合の所要時間(ミリ秒)、+=演算子を使った場合の1要素あたりの所要時間(tick=100ナノ秒)、StringBuilderを使った場合の1要素あたりの所要時間(tick=100ナノ秒)、です。なお1要素当たりの平均文字数は9文字程度です。また、測定は3回おこない平均値を取っています。

この表によるとループ回数が5000回程度であれば所要時間にさほど差違は見られませんが、それ以降は急激に+=演算子の所要時間が増えることが分かります。また、+=演算子はループが5000回より増えるとループ回数が増えれば増えるほど1要素あたりにかかる時間も増えるのに対し、StringBuilderの場合はほぼ一定です。

というわけで1ループあたりに追加する文字数とループ回数が少ない場合は+=演算子でもそれほど問題にはなりませんが、そうでない場合はStringBuilderを使うのが良さそうです。これらの値が増加する可能性がある場合は、最初からStringBuilderを使っておけば、ある日突然処理がめちゃくちゃ重くなる、という事態も避けられるでしょう。PowerShellでStringBuilderを使っているサンプルがネットにはあまり見当たらなかったのですが、PowerShellでも積極的に使うと幸せになれると思います。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/09/20/193089.aspx

2010/06/25

今月のWindows UpdateでVista/XP/Server 2003/Server 2008用のWindows PowerShell 2.0が提供開始になりました。Windows Server 2008の場合では次のような表示になります。

Windows Server 2008 用の Windows PowerShell 2.0 および WinRM 2.0 (KB968930)

ダウンロード サイズ: 32.4 MB

この更新プログラムを有効にするには、コンピューターを再起動する必要があります。

更新プログラムの種類: オプション

Windows 管理フレームワーク コア パッケージには、Windows PowerShell 2.0 および Windows リモート管理 (WinRM) 2.0 が含まれています。Windows 管理フレームワークの詳細については、http://support.microsoft.com/kb/968929 を参照してください。

詳細情報:
http://go.microsoft.com/fwlink/?LinkID=165613

 

公式ブログの記事:Windows PowerShell 2.0 on Windows Update - Windows PowerShell Blog - Site Home - MSDN Blogs

このアップデートは強制ではなくオプションです。このアップデートを適用すると、Windows 管理フレームワーク (Windows PowerShell 2. 0、WinRM 2. 0、および BITS 4. 0) をインストーラーを使用して適用するのと同様にPowerShell 2.0を導入することができます。なお、このインストーラーではPowerShell 1.0があらかじめシステムにインストールされている場合は前もってアンインストールする必要がありましたが、Windows Updateの場合はアンインストールの必要はなく、自動的に1.0が上書きされ2.0になります。なお.NET Framework 2.0 sp1以上がインストールされていない場合、または、正式版でないPowerShellがシステムにインストールされている場合、このアップデートは候補に現れません。

このアップデートはアンインストールすることができます。その場合はコントロールパネルの「プログラムと機能」などで「更新プログラム」を表示し、「Windows Management Framework Core」を選択します。なおこのアップデートをする前にv1.0をインストールしていた場合は、当該アップデートをアンインストールすることでPowerShellのバージョンがv2.0からv1.0に戻ります。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/06/25/190599.aspx

2007/09/02

PS Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT> Copy-ItemProperty Microsoft.PowerShellConsole.1\Shell\Open\Com
mand  -name "(default)" "microsoft.powershellscript.1"
Copy-ItemProperty : 指定されたレジストリ キーは存在しません。
発生場所 行:1 文字:18
+ Copy-ItemProperty  <<<< Microsoft.PowerShellConsole.1\Shell\Open\Command  -name "(default)" "microsoft.powershellscri
pt.1"
PS Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT> Copy-ItemProperty Microsoft.PowerShellConsole.1\Shell\Open\Com
mand  -name "(default)" "microsoft.powershellscript"
Copy-ItemProperty : パス 'HKEY_CLASSES_ROOT\microsoft.powershellscript' が存在しないため検出できません。
発生場所 行:1 文字:18
+ Copy-ItemProperty  <<<< Microsoft.PowerShellConsole.1\Shell\Open\Command  -name "(default)" "microsoft.powershellscri
pt"
PS Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT>

存在するのに。なぜ?存在しないパスを指定すると微妙にエラーメッセージが変わる。HKCU:などでは正常です。アクセス権の問題でもないしなんだろう・・・・うーむ・・・

元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/09/02/93430.aspx

2007/07/21

Get-Helpコマンドレットに-fullオプションを付けると、コマンドレットのパラメータの説明に「必須」、「位置」、「既定値」、「パイプライン入力を許可する」、「ワイルドカード文字を許可する」という項目が追加されます。この中で「パイプライン入力を許可する」がtrueになっている場合は、パイプラインからの入力がそのパラメータに渡されるという意味なのですが、これにはByValueとByPropertyNameの二種類があります(同時に指定されていることも)。

この意味お分かりになられますか?

mixiコミュでいろいろと議論した結果、ようやく分かったのでここでご報告しておきます。

ByValueはオブジェクトがそのまま渡ります。これは特に問題ないでしょう。

ByPropertyNameは、パイプを渡ってきたオブジェクトのプロパティが、パラメータ名と一致した場合、そのプロパティをパラメータとして解釈するという意味です。

具体的にGet-ChildItemコマンドレットを取り上げましょう。

Get-ChildItemコマンドレットは-pathパラメータがtrue (ByValue, ByPropertyName)、-literalPathパラメータ(エイリアスは-PSPath。ちなみにパラメータのエイリアスを調べるには(Get-Command Get-ChildItem).parametersetsのようにするとパラメータの一覧が出ますので、そのAliasを見てください)がtrue (ByPropertyName)です。

よって、入力オブジェクトにPathプロパティがあればその値が-pathパラメータに渡ります。(なければ入力オブジェクトがそのまま-pathパラメータに渡されます)。また、入力オブジェクトにLiteralPathプロパティまたはPSPathプロパティがあれば、-literalPathパラメータにその値が渡ります。これを検証します。

Get-ChildItemコマンドレットの戻り値はファイルシステムプロバイダにおいてはFileInfoオブジェクトとDirectoryInfoオブジェクトを含んだ配列です。これらのオブジェクトにはPSPathプロパティがあるので、この結果をパイプで次のGet-ChildItemコマンドレットに渡すと、そのPSPathプロパティが、-literalPathパラメータに渡ります。すなわちこういうことです。

PS C:\script> Get-ChildItem a*|Get-ChildItem


    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\script


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2007/07/20     18:33          0 a.txt
-a---        2007/07/21     16:17       8826 about_Alias.help.txt

このコマンドに意味があるかどうかは別にして、そういうことが可能だということです。

もっと分かりやすいと思われる例を示しましょう。$aというオブジェクトを作成し、それにAdd-MemberコマンドレットでPathという名前のNotePropertyを追加します。そして$aをパイプラインを通じてGet-ChildItemコマンドレットに渡すとどうなるかご覧ください。

PS C:\script> $a = New-Object PSObject
PS C:\script> $a = $a | Add-Member noteproperty Path "a*" -passthru
PS C:\script> $a.path
a*
PS C:\script> $a|Get-ChildItem

    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\script

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2007/07/20     18:33          0 a.txt
-a---        2007/07/21     16:17       8826 about_Alias.help.txt

というわけで無事、Pathプロパティが-pathパラメータに渡っていることがお分かりいただけると思います。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/07/21/86361.aspx

2006/11/07

cmd.exeでdir /bとすると、カレントにあるファイル名・ディレクトリ名だけを出力します。以下、例。

C:\WINDOWS\system32\windowspowershell\v1.0>dir /b
certificate.format.ps1xml
dotnettypes.format.ps1xml
examples
filesystem.format.ps1xml
help.format.ps1xml
ja
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

同じことをPowerShellにやらせるにはどうすればいいか、考えてみました。

【案1】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|format-wide -c 1

    ディレクトリ: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS\system32\Win
    dowsPowerShell\v1.0

[examples]
[ja]
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Format-Wideコマンドレットを使った例。うーんちょっと違う。でもディレクトリに[]が付くのでわかりやすいかも。これはこれで。

【案2】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|select name
Name
----
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Select-Objectコマンドレットを使ってNameプロパティだけをもったPSCustomObjectのArrayを作っているのでこんな感じに出力されます。
Name
----
が邪魔ですね。

【案3】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|%{$_.name}
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

これでdir /bと同じ結果になりました。Foreach-ObjectでNameプロパティだけを取り出して出力しているわけです。

【案4】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls|split-path -leaf
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

Split-Pathコマンドレットを-leafオプション付きで使っても同様の出力が得られました。これ実は何故こうなるのかよくわからないんですけど、たぶんパイプを渡るときに、System.IO.DirectoryInfoオブジェクトやSystem.IO.FileInfoオブジェクトのNameプロパティが渡されてるか、ToString()メソッドが実行されているのでしょうね。ps1xmlファイルの何らかの記述でこうなっているのかもしれません。でもまあ仕組みが分からなくてもこの動作は非常に合理的であります。

【案5】

PS C:\WINDOWS\system32\WindowsPowerShell\v1.0> ls -name
examples
ja
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershell.exe
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
pwrshmsg.dll
pwrshsip.dll
registry.format.ps1xml
types.ps1xml

ていうかこんなことをしなくても、Get-ChildItemコマンドレットには-nameオプションがあり、dir /bと同じ効果が得られるのでした。ヘルプの見落としでこんな回りくどいことを考えていたのです。すみません、こんなオチで。でもまあ同じことをやるのに色んな手段があるということが分かったので良しとしましょう。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/11/07/43902.aspx


Copyright © 2005-2018 Daisuke Mutaguchi All rights reserved
mailto: mutaguchi at roy.hi-ho.ne.jp
プライバシーポリシー

Twitter

Books