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を、どうぞよろしくお願い致します。

2012/12/01

今日から、PowerShell Advent Calendar 2012が始まりました。初日は私が担当させていただきます。お題は旬の話題、PowerShell 3.0の新機能!…ではなく、初心に返って、PowerShellの「関数」ってどう書くのがいいのかというお話をします。PowerShell 3.0どころか、大部分はPowerShell 1.0から変わっていない基本の話です。

これは今までずっと書きたかったネタですがなかなか書く暇がなくて放置してたものです。3.0の話はきっと他の皆さんが書いて下さるはず!私もまた順番が回ってきたら書こうと思います。

PowerShellの関数は従来言語とだいぶ違う

PowerShellを使いこなすようになってくると、他の言語を使う時と同じで、定型処理は関数として一つにまとめたくなってきます。ところが他の言語と同じような感覚で関数を書くと、どうもうまくいかないのです。

たとえば引数にフォルダパスとフォルダ名を指定すると、指定フォルダが存在すればFalseを返し、存在しなければ作成してTrueを返す関数を書いてみました。

function MakeDir($path,$name)
{
    $newDirPath = Join-Path $path $name
    if((Test-Path $newDirPath))
    {
        return $false
    }
    else
    {
        New-Item -ItemType Directory -Path $newDirPath 
        return $true
    }
}

実行は

MakeDir("C:\test","NewFolder")

と、メソッド風に呼び出すことはできないので、コマンドレット風に

MakeDir C:\test NewFolder

と呼び出せばいいんですが(まあ最初はここもつまづきポイントではありますが)、この実行結果は以下のようになります。

    ディレクトリ: C:\test

Mode                LastWriteTime     Length Name 
----                -------------     ------ ----
d----        2012/12/01      7:51            NewFolder
True

フォルダが作成されてTrueが返却されることを想定していたのに、なんか余計な出力が混じってしまっています。なんでしょうこれは?

実はPowerShell関数内で値が出力されると、returnキーワードがついてなくてもすべて呼び出し元に出力されるという仕様なのです。そしてPowerShellにおけるreturnキーワードの効果は「後続処理を打ち切って呼び出し元に戻る。ただしreturnの後に値が指定してあればそれを最後の値として戻す」となります。そのため、呼び出し元に返したくない出力が関数内にある場合は、すべて[void]にキャストしたり|Out-Nullとしてリダイレクトするなどして出力を破棄する必要があるのです。このMakeDir関数の場合はNew-Itemコマンドレットが作成したフォルダのFolderInfoオブジェクトを出力するので、これをNew-Item -ItemType Directory -Path $newDirPath | Out-Null のように破棄してやる必要があるわけです。

パイプラインの動作

先ほどの例を見ると、「いやいやなんでそんな訳のわからない仕様なんだよ、returnあるときだけ値返せよ」とお思いかと思います。しかしこれはPowerShellの特長の一つである、コマンドのパイプラインによる連携を行うための仕様なんです。

ここでコマンドを繋ぐパイプラインがどういう動作をしてるか、おさらいします。

Get-Process | where {$_.Handles -ge 500} | foreach {$_.Path}

これはハンドル数が500以上のプロセスのメインモジュールファイルのパスを取得するというコマンドで、別に何の変哲もありません。ところが、このコマンドがやっている処理を、次のように誤解してませんでしょうか?

@ 稼働中のすべてのプロセスの一覧を配列として取得する。
A @で取得した配列を走査して、Handlesプロパティの値を調べる。Handlesが500以上のオブジェクトだけ抽出した配列を生成する。
B Aで生成した配列を列挙して、{}内のスクリプトをそれぞれ実行する。

しかし、これは間違いです。

正しくは

@ 稼働中の1つのプロセスオブジェクトを取得して次のコマンドへ送る。
A そのプロセスのハンドル数が500以上なら、次のコマンドへ送る。そうでないなら@に戻る。
B そのプロセスに対して{}内のスクリプトを実行する。まだ未取得のプロセスが残っていれば@に戻る。

という動きをしています。つまり、パイプラインの手前で一旦すべての処理を終えてから、出力オブジェクトがまとめて配列という形で次のコマンドに送られるのではなく、オブジェクトがパイプラインの先頭から末尾に向けて1つずつ通過していき、それが先頭コマンドの出力オブジェクト数だけ繰り返される、という動作をしているのです。

これがPowerShellのパイプライン処理が、従来の処理系での関数と決定的に違うところで、パイプラインによって複数のコマンドが、あたかももとからあった単一のコマンドのように密に連携するわけです。

(この処理、.NETのLINQにちょっと似てると思う方もいらっしゃると思います。しかしLINQとは全然違うものです。なんせPowerShellはLINQより先に世に出てますし! しかし類似点も多いのでいずれ比較なんかを書きたいと思ってます)

パイプラインで連携可能な関数の書き方

さて、先ほどのパイプラインの話ではコマンドレットを連携させていました。しかしPowerShellにおいてはコマンドレットも関数も、それが.NETのクラスかPowerShellのスクリプトなのかの違いがあるだけで、基本は同じ「コマンド」です。なので、関数もコマンドレットと同様、適切な記述をおこなえば、パイプラインでコマンド同士を連携させることが可能です。

以下に、Get-Repeatという関数の例を挙げます。この関数は-Textパラメータに文字列を指定し、-Countパラメータに回数を指定すると、指定文字列を指定回数分連結した文字列を出力する、という何の変哲もない関数です。しかしパイプラインからの入力を受け付け、次のパイプラインへ出力することを想定した作りになっています。

function Get-Repeat
{
    param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [string[]]
        $Text,
        
        [int]
        $Count=2
    )

    begin
    {
    }

    process
    {
        foreach($s in $Text)
        {
            $s * $count
        }
    }

    end
    {
    }
}

以下は実行例です。

PS> Get-Repeat -Text ab -Count 2
abab
PS> "ab" | Get-Repeat -Count 2
abab
PS> Get-Repeat -Text ab,cd -Count 2
abab
cdcd
PS> "ab","cd" | Get-Repeat -Count 2
abab
cdcd

このように、パラメータに値を指定してもパイプラインから入力しても、スカラー値(配列ではない単一のオブジェクト)でも配列でも、正しく処理されています。

この関数をポイントごとに見ていきましょう。

PowerShellの正式な関数はparam節、beginブロック、processブロック、endブロックに分かれます。param節にはパラメータを指定します。beginブロックにはパイプラインで連携した際、最初の1回だけ実行される初期化処理、endブロックには最後の1回だけ実行される後始末処理を記述します。beginとendは今回の例では内容を省略しています。processブロックには、パイプラインから入力された1つのオブジェクトに対してその都度実行される処理を記述します。

ちなみに、

コマンド@|コマンドA|コマンドB

とある場合、各コマンドにおけるbegin,process,endブロックは次のような順番で呼び出されます。

コマンド@begin→コマンドAbegin→コマンドBbegin→{コマンド@process→コマンドAprocess→コマンドBprocess→コマンド@process…}→コマンド@end→コマンドAend→コマンドBend

processブロックでの処理は、通常はパイプラインだけではなくパラメータからも値を入力できるようにしておきます。そのためにはparam節に記述するパラメータに「このパラメータはパイプラインから値を入力することもできる」を意味する[Parameter(ValueFromPipeline=$true)]という属性を指定します(この属性はPowerShell 2.0から利用可)。今回のパラメータには「このパラメータは必須である」を意味するMandatory=$trueもあわせて指定しています。

先述の通り、パイプラインから入力される場合は配列ではなくオブジェクトが単体で渡されるのですが、パラメータから入力される場合はスカラー値と配列値、どちらの可能性もあるため、[string[]] のようにパラメータの型を配列型にしておくことで、どちらを指定しても処理できるようにしています。

processブロックではパラメータ経由で配列値が渡された場合に、各要素に対して処理を行うためforeachループを設けています。ちなみにスカラー値が渡された場合もforeachは問題なく処理します。

processブロック内では、returnは記述しません。returnするとその時点で関数が終了してしまうので正しくすべての出力ができなくなってしまいます。

特にこの例の関数のように入力型と出力型が同一の場合は、processブロックでは1オブジェクトの入力に対して、1オブジェクトを出力するようにしておくと、他のコマンドと連携させやすくなります。ただしWhere-Objectコマンドレットのようにフィルタ処理を行う関数の場合は、条件によっては何も出力しないようにします(空の配列とか$nullを返すのではないことに注意)。もちろん入力オブジェクトから何らかの配列値を出力する場合もありえます。

最低限、これらのポイントを押さえて関数を記述すると、他のコマンドとパイプラインで連携しやすい、PowerShellらしい関数を書くことができると思います。

まとめ

PowerShellでは従来言語と同じ感覚で関数を書くと、うまくいかないことが多いです。もっとも単に処理をひとまとめにしたいというニーズだけならばそれでも問題ないのですが、関数同士を組み合わせたいときに問題が顕在化します。

パイプラインの真の動作を理解し、パイプラインの中に組み込んで動作させることを想定した関数を記述すると、他のコマンドレットあるいは自作関数と連携しやすくなり、PowerShellの真の力を解放することができると思います。

PowerShell Advent Calendar 2012の1日目にしてはえらい固いネタかもですが、基本をおさらいするのも大事ですよね。

さて、明日は@jsakamotoさんの番です。よろしくお願いします。

2012/02/14

2/25(土)に開催されるわんくま同盟 大阪勉強会 #47で、「PowerShell 3.0 概要」と題してPowerShell 3.0のセッションをします。

PowerShell3.0はWindows 8とWindows Server 8に搭載され、従来のコンソールexeの大半がOS付属モジュールのコマンドレットに置きかわります。その数Win8では800以上、Server8では2000を超える量になります。これまでのexeも互換性のために残されますが、今後の主流は間違いなくPowerShellコマンドレットにシフトしていきます。

Windows Server 8では「役割」と「機能」に対応するPowerShellモジュールがそれぞれ用意され、管理用GUIがPowerShellモジュールの機能を呼び出す構造への移行がより一段と進みます。

Windows Server本体のみならず、サーバーアプリケーションでもMicrosoft製品はもちろん、サードパーティ製のものでもPowerShellモジュールが付属しているのが珍しくなくなりました。またOffice365やWindows AzureといったクラウドサービスもPowerShellで管理できるようになってきました。

PowerShellの利用場面の増加に答えるように、PowerShell3.0ではさまざまな改善と機能追加が行われています。今回のセッションではそれらをご紹介します。Windowsを管理する上でPowerShellはもはや必須といえる段階になってきたと思います。この機会にPowerShellを始めてみませんか?

今回は先日MVPを受賞されたちゅきさんのActiveDirectoryの話やmoririringさんによるC#開発中級編の話、TWorksさんによるスマフォやタブレットの開発をHTML5対応JSフレームワークで行う話など、もりだくさんです。お時間がございまいたら2/25はぜひ、わんくま勉強会へお越しください!

2009/01/04

仕事・私事ともにごたごたしてまして大変なのですが、皆様のおかげでなんとか年を越せました。

今年は、いろいろ見えないところも多いんですが、やはりScript系技術を中心に追っていこうと思っています。PowerShell v2を追いたいですし、DLRというかC#4.0/VB10/.NET 4.0がスクリプトに近いものになるのか見届けたいですし。レガシなASPやWSHもまだまだ遊べそうです。本業プログラマ副業ライター趣味スクリプト、という感じでやっていこうかと。あ、ディープな趣味はhttp://winscript.jp/blog/ でw

ここでTips。

Vistaを再インストールしたんですが、OfficeのSelfCert.exeによるコード署名ができなくなってしまいました。その復旧方法。

C:\Users\ユーザー名\AppData\Roaming\Microsoft\Crypto\RSA

を削除。以上。異常になったときも試してみるといいのでは。

だいたいAppDataをバックアップするのが間違いなんですがね。副作用が多いのですが環境がすぐに復帰するので私はバックアップしてます。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/01/04/165530.aspx

2008/11/22

サイドバーにTwitterのタイムラインを表示するtwitgadetというガジェットのがあるんですが、返信タイムラインを取得する機能があるのに呼び出すのを忘れているというバグ?実装漏れ?をみつけたのでfixしてみます。

C:\Users\<UserName>\AppData\Local\Microsoft\Windows Sidebar\Gadgets\twigadge-1.1[1].gadget\js\twigadge.js

とりあえず現状では返信タイムラインが呼ばれないのは間違いないです。とりあえず返信タイムラインを呼ぶようにfix。367行目あたり

      Twigadge.getFriendsTimeline();

     Twigadge.getReply();

に変更。

533行目あたり。

          Twigadge.refreshTL(); //

          Twigadge.getFriendsTimeline(); //

に変更。

C:\Users\<UserName>\AppData\Local\Microsoft\Windows Sidebar\Gadgets\twigadge-1.1[1].gadget\js\gadget.js

90行目あたり

        Twigadge.getReply();

の下に次の行追加

        Twigadge.refreshTL();

これでとりあえず返信タイムラインは取得できます。

ダイレクトメッセージも設定に関係なく最初の一回しか呼ばれないっぽいです。
これを直すには

C:\Users\<UserName>\AppData\Local\Microsoft\Windows Sidebar\Gadgets\twigadge-1.1[1].gadget\js\twigadge.js

658行目あたり

      case MODES.system:
        Twigadge.refreshTL();
        break;
      case MODES.dm:
        Twigadge.refreshDM();
        break;
 

      case MODES.system:
        Twigadge.refreshTL();
        Twigadge.refreshDM();
        break;
      case MODES.dm:
        break;

とします。

これで作者さんの意図どおりかわかりませんが、とりあえず説明書通りには動きます、たぶん。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2008/11/22/161950.aspx

2008/03/30

powershell-cover

というわけで今日の講演でお披露目した、技術評論社さんのほうで書いていた本が発売決定なのでご報告ですー

ご購入していただける方はうちのサイトからしていただくとちょっと嬉しいです。

第一部PowerShellの基礎100p、第二部コマンドレット200p、第三部.NETクラス・構造体100p計400pの三部構成です。

プログラム初心者からUNIXシェルを使いこなしている方まで幅広くお使いいただけると思います。ヘルプの間違いも可能な限り直っています。よろしくですー

あとかわいい女の子の絵もカットで入ってますよ!

元記事:http://blogs.wankuma.com/mutaguchi/archive/2008/03/30/130459.aspx

Copyright © 2005-2016 Daisuke Mutaguchi All rights reserved

mailto: mutaguchi at roy.hi-ho.ne.jp

Awards

Books

Twitter