コマンドレットのパラメータを列挙してみる。」に基づいた記述が含まれているおそれがあります。2017の24日目です。一般的なプログラミング言語では、文(statement)と式(expression)の違いは、値を返すのが式で、返さないのが文、という説明がされることが多いと思います。PowerShellではこの説明は成り立たたず、文が値を返したりしてるように見えて良く分かりません。3.0のものですが)やAST(ShowPSAstモジュールが便利!)を眺めながら考えてみたので軽くまとめようと思います。文(statement)と式(expression)の定義PowerShellでは言語要素として、文(statement)と式(expression)が明確に定義されています。言語要素の何が文であって、何が式であるかという定義は仕様できちんと決まっていて、ある言語要素が、状況によって文になったり式になったりと変化する、ということはありません。パイプライン、代入、ifやforやfunctionなどは文です。変数、数値/文字リテラル、オブジェクトのメンバ呼び出し、スクリプトブロック、単項または二項算術演算子で構成される式、カンマ演算子で構成される配列などは式です。仕様書での定義と、実際に構築されるASTに齟齬があることはあります。assignment-expressionと書かれています。厳密には、言語仕様書のgrammer節によれば、assignment-expressionはexpressionではなくpipelineであるということになっています(お前は何を言っているんだ)。いずれにせよパイプラインは文であるので、AST通り、代入は文であるという解釈で良いと思います。expressionとはっきり書いてあるんだから代入は式だろ!という意見を否定するものではないです。代入は文であると考えたほうが他の文法と整合性を取りやすいので、そういう立場をとりました。しばたさんが9日目に書かれた記事で取り上げられているように、配列に関しても同様の齟齬があります。いずれにせよ「1,2,3」のような配列は、式と考えて良いと思います。文と式の構造PowerShellには文と式が存在することは分かりました。それを考察するために、具体的にいくつかの文と式の構造を取り上げて見ていきます。ProcessName」みたいな|で繋ぐやつのことでしょ、と思われがちですが、言語仕様上は以下のようなものはすべてパイプラインです。�E算術式をパイプラインでコマンドと繋げたもの要はパイプラインというのは、一般的なプログラミング言語で、;で終わる一文に相当するものと考えればだいたい間違いないと思います。PowerShellだと文末の;は必須ではなく、改行でもOKです。パイプラインは以下のような構造を取ります。[]は省略可能を意味します。代入文すなわち、代入文(後述)を除くパイプラインは1個以上のパイプライン要素から構成されており、複数のパイプライン要素が存在する場合はパイプ演算子|で連結されます。パイプライン要素には式とコマンド(Get-Processとか)が存在します。式は1つ目のパイプライン要素にのみ許可されます。以上を踏まえると、�@、�Dはコマンドのみで構成されるパイプライン、�A、�Bは単独の式のみで構成されるパイプライン、�Eは1つの式とコマンドで構成されるパイプライン、�Cは代入文であることが分かります。代入文はパイプラインなので、文です。代入文は以下のような構造を取ります。文ただし、左辺の式は、変数やプロパティなど、代入が可能な式である必要があります。よって以下のような記述が可能です。右辺には文であれば何でも書けるのですが、実際に代入が行われるのは、パイプライン、if文、for文、switch文といった、パイプラインに値を出力する文と代入文に限られます。ちなみに�Dのようにパイプラインと代入文以外の文を右辺に指定できるようになったのは、PowerShell2.0からです。ところで上記�@や�Aは右辺が式です。代入文の右辺は文じゃなかったの?と思われると思いますが、パイプラインの節で述べた通り、単独の式もパイプラインであり、パイプラインは文なので、三段論法でいくと式は文として扱われることになります。$bの右辺はPipelineAst/CommandExpressionAst/VariableExpressionAstではなく、いきなりCommandExpressionAst/VariableExpressionAstとなっているので、この説明はASTの実装とはかみ合わないかもしれません。AssignmentStatementAst.Rightは確かにStatementAstを取るのですが、CommandExpressionAstはStatementAstから派生しているクラスなので、式の代入は問題なく行えます。代入文は上記�Eのようなことができることから分かる通り、値を返す文ですが、パイプラインには値を出力しません。値は返すがパイプライン出力がないものは、インクリメント演算子で構成される式($a++等)も同様です。if文パイプラインと代入文以外の文は色々あるわけですが、代表的なものとしてif文を取り上げます。シンプルなif文はこういう構造です。{文1文2....}おそらく多くの人が誤解しているのではないかと思いますが、条件節に書くのは式ではなくパイプラインです。パイプラインを実行した結果、出力値がtrue、またはboolに型変換してtrueになる場合に、ブロック内の複数の文が実行されます。よって以下のような記述が可能です。�@と�Aは普通の書き方ですが、実際には、1つの式のみ有するパイプラインを実行し、出力される値が判定されています。条件節はパイプラインなので、当然�Bのような書き方もできるわけです。代入文もパイプラインであるので、�Cの書き方もできてしまい、注意を要します。(if($true){}){}というような書き方はできません。※といっても実はこう書くと文法上はvalidであり、条件節内は「ifコマンド、パラメータ値1($true)、パラメータ値2(スクリプトブロック)」という解釈になってしまいます。{}という書き方はできません。(パーサーもエラーを出す)丸括弧式さて、普通のプログラミング言語だと、()はグループ化や演算子の優先順を変更するのに用いられるものの、別に文法そのものに影響を与えるものではないと思います。多くの場合、ASTでも()の情報はそぎ落とされます。ところがPowerShellでの丸括弧()は文法的な意味を有しており、ASTにもParenExpressionAstとして存在する、立派な式です。丸括弧式の構造は以下の通りです。(パイプライン)これは要するに、「パイプラインに()を付けると式になる」、ということです。()内のパイプラインで出力された値が返される式となります。具体的にどういうところで使うのかを示します。�Cコマンドのパラメータにコマンド実行結果を指定する�@の使い方は普通です。()はパイプラインを生成するので、「(1+3)」は「1つの式のみ有するパイプラインを実行し、パイプラインに値を出力し、その値を返す」という見た目より複雑な処理になります。※少なくともAST上はそうなりますが、実際は何らかの最適化処理が入ってる可能性はあります。代入を重ねる場合には�Aのような書き方は必要ないのですが、代入した結果をパイプラインの出力としたい場合は()を付ける必要があります。この場合、$aに1が代入され、コンソールにも1が出力されます。�Bで挙げている、式を許容するがパイプラインは許容しない言語要素というのは実はあまりないです。前述した通りif文の条件節は式じゃなくて、パイプラインを取るといった案配です。配列や連想配列の要素を取得するインデックス演算子[]は、式のみ許容されます。コマンドなどのパイプラインの出力値を指定したい場合は()が必須となるわけです。ちなみに、()内にはパイプライン以外の文(if文等)は指定できません。複数のパイプラインも指定できず、あくまで1つだけです。※任意の文あるいは複数の文を式としたい場合には、部分式演算子$()または@()を用います。両者とも内部の文がパイプラインに出力した値を返す、「部分式」となります。両者とも複数値が出力されると配列になりますが、@()は出力値が1つでも要素数1の配列を返す点が異なります。まとめ?PowerShellの文と式は厳密に定義されています。文は複数の文と式で構成されるし、式は複数の文と式で構成されています。文や式の構成要素が取る文や式の種類についても、各々、きちんと定義されています。PowerShellにおいて「値を返すか返さないか」、「パイプラインに出力されるかされないか」、「式であるか文であるか」という概念はすべて独立しています。そのため、PowerShellの文とは何である、式とは何である、ということを一言で説明することは難しいんじゃないかと思います。本記事でこれまで述べてきたとおり、「パイプラインは文で、要素として式やコマンドを取りますよ」とか、「ifは文で、条件節にはパイプラインを取りますよ」みたいな、各論でしか表現できないのではないかなぁと、私はそういう結論に至りました。ここまで書いてちゃぶ台をひっくり返すようなことを言いますが、ある言語要素が文であるか式であるか、ここまで仕様書を読んだりASTを追ったりして把握するのは、まあ楽しくなくはないですが、知らなくても別に大丈夫だと思われます。別に、ifの条件節には条件式を書くのだと理解していても不都合は特にないかと。{}」とかの、余分な()を取り除くのには文法の知識が役立ちます。それ程問題にはならないかもしれません。2017の10日目です。PowerShellはオブジェクトを扱うシェルですが、別にテキストデータを扱えない訳ではありません。PowerShellで取得したデータをテキストファイルとして保存したり、スクリプトで用いるデータをテキストファイルで保存しておくことは日常的に行われることだと思います。PowerShellで扱うデータはオブジェクトであり、テキストファイルは文字通り文字列であることから、コマンドレットを用いる等、何らかの手段で変換が必要になります。テキストデータ形式にも様々な種類があり、それぞれメリット、デメリットが存在します。今回の記事では、PowerShellで用いるデータを保持しておく際のテキストデータ形式について比較をしてみます。プレーンテキストプレーンテキスト、すなわち書式なしのテキストファイルです。シンプルな使い方をする場合、文字列配列の各要素に含まれる文字列が、テキストファイルの1行と対応します。UTF8$linesは文字列変数です。理由がなければ文字コードはUTF-8で良いと思います。UTF8Add-Contentは実行のたびにファイルを開いて、書き込んでから閉じるという動作をするので、1行ずつforeachで実行するのはNGです。UTF8)メリット文字列配列をテキストファイルに書き出すのは多分これが一番楽だと思います。書き出したデータは人間にも読みやすい。デメリット文字列だけを保存しておきたいというケースがそもそも少ない。CSVコンマ等の特別な文字で区切り、1行あたりに複数のデータを保存できる形式です。PowerShellのコマンドレットで扱う場合、オブジェクトが持つプロパティがヘッダ列名に対応します。各行にオブジェクト配列1要素のプロパティ値が、カンマ区切りで保持されます。Default$objectsは任意のオブジェクト配列です。必要であればSelect-Objectコマンドレットを併用して、プロパティを絞り込みます。文字コードはExcelでそのまま読み込み/書き出しができるDefault(日本語環境ではShift_JIS)がお勧めです。Defaultメリットオブジェクトのプロパティ値が、すべて数値あるいは文字列で表現できる値を持つ場合に最も適合する。人間にも読みやすく、ある程度は編集もできる。デメリットオブジェクトのプロパティが、数値と文字列以外のオブジェクトである場合、すなわち、階層構造を持つデータの保存には適さない。数値も文字列として読み込まれてしまうので、数値として扱いたい場合は変換が必要になる。Export-CsvとImport-Csvで扱うCSVファイルはヘッダが必須。ヘッダなしのCSVファイルが既にあって、それを読み書きするという用途には適さない。(できなくはないが)書き出し時の列順を制御することができない。PowerShellで書き出したCSVを、列順が固定であるとの想定である他のプログラムで読み込むことは基本NG。書き出し時、1つ目の要素に存在しないプロパティは、2つ目以降では存在しないものとして扱われる。同種のオブジェクトで構成される配列なら通常は問題ないのだが、要素によって動的に追加されるプロパティがあったりなかったりすると厄介。(ADでありがち)JSONJavaScriptのような表記でデータを保持するデータ形式です。データの受け渡しに様々な言語で利用できます。APIでもよく利用されます。PowerShellではv3からJSONを扱うコマンドレットが提供されています。ConvertFrom-JsonメリットCSVと異なり、階層構造を持ったデータでも扱える。CSVと異なり、数値は数値型のまま読み書き可能。(整数値はint、小数値はdecimal)人間にもまぁまぁ読めるし、頑張れば編集できなくもない。デメリット-Depthパラメータによりプロパティを展開する階層の深さを指定はできるが、プロパティに応じて深さ指定を変化させるというようなことはできない。基本的には、自分で構築したPSCustomObjectを使うか、JSON化する前に自分で元オブジェクトを整形しておく必要がある。直接ファイルに書き出し、追記、ファイルから読み込みするコマンドレットはない。実は細かい話をしだすと色々と罠があります...。CLIXMLPowerShellではPSリモーティング等、プロセス間でオブジェクトのやり取りを行う際に、CLIXML形式を介してシリアライズ/デシリアライズが実行されます。シリアライズ対象によっては、完全に元のクラスのオブジェクトに復元されます。(復元されないオブジェクトにはクラス名にDeserialized.との接頭辞が付与され、プロパティ値のみ復元される)ユーザーもコマンドレットを用いて、任意のデータをCLIXML形式でシリアライズし、XMLファイルとして保存することができます。file.xmlメリット元のオブジェクトの構造、プロパティ値と型情報を含めてほぼ完全にテキストファイルに保存できる。復元したオブジェクトはプロパティ値を参照できるのはもちろん、オブジェクト全体が完全にデシリアライズされ、元の型に戻った場合には、メソッドを実行することも可能。元の型に戻らず、Deserialized.との接頭辞が付いた状態でも、コンソールに表示する場合は元の型のフォーマットが使われるので見やすい。デメリットすべてのオブジェクトが元の型に戻せるわけではない。戻せるかどうかは確認が必要。人間が読み書きするようなものではない。ちなみに、ConvertTo-Xmlという似たようなコマンドレットがありますが、出力形式はCLIXMLではない上、復元の手段もなく、かといって別に読みやすいXMLというわけでもなく、正直何のために使うのかよく分かりません(適切なxsltでも用意すればいいのかな?)。ConvertTo-Htmlの方が使えそうです。psd1psd1は「PowerShellデータファイル」で、モジュールマニフェストやローカライズデータに使われるファイル形式です。コメントなど基本的な言語要素のみ使用可能です。5.0以降ではImport-PowerShellDataFileコマンドレットを用いて、任意のpsd1ファイルのデータを読み込み、変数に格納することが可能です。書き出し書き出し用のコマンドレットはありません。読み込み例えば以下のような内容をbackup_setting.psd1として保存しておきます。ルート要素は必ず連想配列にします。"0:00"}なお、dataセクションで全体を括ってもいいですが、psd1で許容される言語要素はdataセクションより更に制限がきついので、敢えてしなくてもいいんじゃないかと思います。このファイルは以下のように読み込めます。backup_setting.psd1$settingには連想配列が格納され、以下のように値が参照できます。$_.To}メリットPowerShellの構文でデータを記述できる。通常のps1ファイルを呼び出すのとは異なり、式の評価やコマンド実行などはされない分、セキュアである。配列と連想配列の組み合わせにより、JSONライクな階層構造を持てる。JSONとは違い、コメントが入れられる。デメリット記述できるデータはプリミティブなものだけ。スクリプトから書き出すためのコマンドレットがない。一応、下位バージョンでやる方法はあります。まとめPowerShellで扱うデータをテキストファイルとして保存する際には、各テキストデータ形式の特性を理解し、メリット、デメリットを踏まえて選定する必要があります。当然ながらテキストファイルに保持することが不適切なデータもありますので、そこは注意してください。(画像データを敢えてBase64とかでエンコードしてテキストファイル化する意味があるのか、とかですね)個人的には...ちょっとした作業ログ等を記録しておきたい→プレーンテキスト.NETオブジェクトの一部のプロパティだけ抜き出してファイル化したい→CSV自分で構築したPSCustomObjectをファイル化したい→JSON.NETオブジェクト全体をファイル化したい→CLIXMLスクリプトで使う設定データを用意したい→psd1みたいな感じでなんとなく使い分けていると思います。psd1はまだ採用例はないですが...。今回はビルトインのコマンドレットで扱えるもののみ取り上げましたが、他にもyaml等のテキストデータ形式が存在し、有志によるモジュールを用いて扱うことが可能です。2017の1日目です。Calendar、今年も始まりました。ここ数年は私がトップバッターを務めさせていただいて、1年間のPowerShell界隈の出来事をさくっとまとめてみています。Editionが各々正式版としてリリースされ、さらにはPowerShellがオープンソース化、マルチプラットフォーム展開を始めるという大きな変革があった年でした。今年2017年は昨年ほど大きな変化はないとはいえ、昨年のOSS化からのマルチプラットフォーム展開を着実に進行させた年だと言えると思います。以下、いくつかトピックを紹介します。Updateにも同梱されました。R2)のためのものです。Win10/2016に同梱のPester(テストフレームワーク)やPSReadline(コンソール入力支援)についてはWMF5.1には含まれていないので、別途PowerShellGetでインストールするのがお勧めです。ShellではまずBashがサポートされていましたが、今年9月にPowerShellもサポート(まだプレビューですが)されました。PowerShellのコマンドが使え、AzureのリソースにAzure:ドライブを介してアクセスすることが可能です。5.1であることはちょっと念頭においておいたほうがいいかもしれません。PowerShellの全機能がサポートされてないからだと思うんですが、今後に期待ですね。Codeの正式版(1.0)が5月に公開されました。現時点での最新バージョンは1.5.1となっています。ISEの方が多機能だったようにも思いますが、今はもう完全にISEの機能を追い越したんじゃないかと思います。Analyzer、テストフレームワークのPester、プロジェクト管理機能を提供するPlasterなどが統合されており、本格的な開発環境となっています。Coreの開発が各々可能です。VSCodeの開発に注力されることになります。ISEはとにかく標準添付である(GUI有効ならサーバーOSでも動く!)という強みがあり、シンプルなスクリプト記述であればそこそこ便利に使えるので、これからもシチュエーションに応じて使い分けて行けば良いのかなと思います。Candidate)となりました。6.0.0のGAリリースは来年1月になるそうです。OSS化直後からRCに至るまでの変更点は多岐に渡り、とても一言で説明できるものではないですが、ポイントとしては以下の3点に集約されるんじゃないかと思います。5.1のコードそのままであったと言ってよいかと思います。10年以上増改築が繰り返されたコードが突如、全世界に公開されたわけです。コミュニティの力でバグや変な仕様といった問題点が洗い出され、どんどん修正されていきました。不足していると思われる機能はどんどん追加されました。既存コマンドレットのパラメータが増えるというパターンが多かったように思います。特筆すべきは、破壊的変更であっても妥当性があれば躊躇せずに取り入れていったことかと思います。Coreでは細かいところで非互換性が色々出ていますので、移行の際には注意を要します。5.1なので、Windowsでしか動作しない部分が多々ありました。それをLinuxやMac環境でも動作するように多くの修正が加えられました。Edition相当のPowerShellをビルドすることが可能でした。Core」であることが明確にされました。CLR対応のコードのみとなりました。5.1に含まれているすべてのコマンドレットを網羅しているわけではありません。6.0には含まれておらず、α版の段階では実行も不可能(だったはず)でした。2.0がサポートされました。Coreでも実行可能になりました。PowerShellはどうなるのか、という話について。5.1については、今後もサポートライフサイクルに則り、重大なバグフィックスやセキュリティパッチ提供等のサポートは継続されます。PowerShellも同様です。Coreへと移行します。PowerShellとは無関係ということです。CoreがWindowsに同梱されるかどうかについては言及されておらず、今のところは不明です。以下は私見になります。Serverの管理はするが、Linuxとかは特に...というユーザーはこれからどうすべきか?という点は割と悩ましいところだと思います。Serverを管理するスクリプトが現時点であるなら、それを無理に今すぐCore対応にする必要はないと思います。現時点で今すぐCoreに移行すべき理由というのはとくに無いと感じます。Coreで追加、改善された機能はあるものの、Coreには無い機能もたくさんあるからです。PowerShell用に作れば良いのではないかと。OS付属のコマンドレットの動作は確実に保証されているわけですから。2016は半期に一度の大型アップデートで新機能が次々追加されていきます。Coreが含まれるようになったり、Coreのみ対応のコマンドレットが追加される可能性は無きにしもあらずなのではないかとも思います。Coreの状況をチラ見しつつ、未来に備えておく必要はあると思います。Windows10/Server2016の「次」も見据えて。それとPowerShellでWindowsもLinuxも面倒みていきたい、という野心がある方は、Coreを採用していくのがいいのではないかと思います。現状しばらくは茨の道ではあるとは思います。あとはスクリプトやモジュールを作成し公開する方は、より多くの環境で使われるように、可能であればCore対応を進めるのは悪くないんじゃないかと思います。Core構想とか、なにげに結構いろいろ話題はありました。Coreとしてはこれからも活発に動いていくものと思います。注目していきたいですね。そんな2017年の締めくくり、今年はどんな記事が集まるでしょうか。2017の参加、お待ちしております。2016の20日目です。はじめに前々回はASTの概要について述べ、最後にAST.FindAllメソッドを使って、ASTから指定のASTノードを検索する方法について説明しました。前回はASTを再帰的に検索して、木構造を視覚化してみました。Visitorを用いる方法について説明します。お断りしておきますが、静的解析の実装までは今回はたどり着きません。静的解析ツールをどう作るかorどう作られているか、ということを雰囲気で味わっていただければと。Visitorの説明をする前に、まず、Visitorパターンについて簡単に。Visitorパターン[Wikipedia]というのは、オブジェクト指向言語におけるデザインパターンの1つで、対象オブジェクトを巡回する「訪問者」クラスを定義するものです。Visitorクラスでは、対象クラスごとに行う処理を、個別にvisitメソッドをオーバーロードさせることで定義します。共通のVisitor抽象クラスを継承することで、異なる機能を持ったVisitorクラスを作ることができます。処理対象クラスには、Visitorオブジェクトを引数に受け取る、acceptメソッドを定義します。acceptメソッドでは、引数として受け取ったVisitorオブジェクトのvisitメソッドを呼ぶことで、処理を実行させます。処理対象クラスが子要素クラスを持つ場合には、acceptメソッド内で、子要素クラスのacceptメソッドを呼ぶようにします。しておくことで、Visitorは処理対象を再帰的に巡回できるようになります。このように処理対象クラスから、実際に処理を行う機能をVisitorクラスとして分離することで、処理対象クラスに手を加えることなく、Visitorクラスを追加して、処理内容を増やしたりすることが可能になります。Visitorの呼び出し方を見ていきましょう。Ast抽象クラスには、以下の2つのVisitメソッドが定義されています。astVisitor)説明に入る前に注意点。メソッド名は"Visit"となっていますが、Visitorパターンでいうところの"accept"メソッドのことです。メソッド名がAcceptじゃないのかは不明ですが...。AstクラスのVisitメソッドは、AstVisitor抽象クラスを継承したクラスのオブジェクトか、ICustomAstVisitorインターフェースを実装したクラスのいずれかを引数に取ることで、ASTに対する処理を実施します。AstVisitor抽象クラスを継承、もしくはICustomAstVisitorインターフェースを実装することで、ASTの種類に応じた巡回処理を行うクラスを、自分で定義していきます。AstVisitor抽象クラスAstVisitor抽象クラスは、Visitorとしての基本的な機能があらかじめ実装されています。具体的には既に以下の機能は用意されています。ASTの種類に応じたVisitメソッドの定義すべての種類のASTに対応するVisitメソッド(50個以上)がVirtualメソッドとして定義されています(※)。IfStatementAstに対する処理を行うための、VisitIfStatementメソッドがあります。※一般的なVisitorパターンでは、Visitメソッドを対象クラス分オーバーロードさせますが、PowerShellのAstVisitorは対象クラスに応じた別名のメソッドを定義する方式です。これも理由は分かりませんが、オーバーロードにするには多すぎるからかもしれません。各Visitメソッドには、ASTの子ノードに対し、再帰的にVisitメソッドを呼ぶ仕掛けがあらかじめ備わっています。各VisitメソッドはAstVisitAction列挙型を返却します。以下のように返却する値によって、ノード巡回の継続、停止を制御できます。Continue:ノード巡回を継続(デフォルト)SkipChildren:子ノードの巡回を行わないStopVisit:巡回を終了するカスタムAstVisitorクラスを作成する以上の基本的な機能を踏まえて、AstVisitor抽象クラスを実装したカスタムVisitorクラスを作ります。v5で追加された、クラス構文を使って書いてみましょう。「利用しているコマンドのリストを取得する。コマンドのパラメータ内で別コマンドを呼び出している場合は除く。」というお題を解くことを考えます。ASTのFindAllメソッドだと、配下に含まれるすべてのCommandAstを取得してしまうので、単純にはいきません。そこでカスタムAstVisitorクラスの出番です。このお題を実現するVisitorクラスは以下のようになるでしょう。}}PowerShellのクラス構文において、Virtualメソッドのオーバーライドは、単に同名のメソッドを定義するだけですので、ここではVisitCommandメソッドをオーバーライドします。プロパティとフィールドの区別はないので、コマンド名の一覧を格納するCommandNamesプロパティは上記のような定義になります。メソッド内でクラスメンバを参照する際には$thisを用います。作成したGetCommandNamesVisitorクラスをインスタンス化し、解析対象スクリプトブロックのASTのVisitメソッドに引数として渡します。GetCommandNamesVisitor$scriptBlock.Ast.Visit($visitor)$visitor.CommandNames実行すると、結果はGet-ChildItemSort-ObjectSelect-Objectのようになるかと思います。AstVisitorクラスの具体的な実装については、PSReadLineやPowerShellEditorServicesにありますので、参考にしてみてください。前項で述べた、AstVisitor抽象クラスを継承したカスタムAstVisitorクラスの場合、基本的な処理を実装する必要はないですし、目的とするASTクラスに対するVisitメソッドだけオーバーライドすればいいので、非常に簡便です。本格的にPowerShellの構文解析を行いたい場合、ノードの巡回順だとか、その他もろもろをもっと細かく自分で実装したいケースが出てきます。いった場合にはICustomAstVisitorインターフェースを実装したクラスを作って対応します。ICustomAstVisitorインターフェースも、AstVisitor抽象クラス同様、各ASTクラスに応じたVisitメソッドが定義されているのですが、各VisitメソッドはAstVisitAction列挙体ではなく、object型のオブジェクトを返します。自分で好きなオブジェクトを返すように定義できるわけです。Ast.Visit(ICustomAstVisitor)はAstVisitor抽象クラスを引数に取る場合と異なり、objectを返却するのですが、このとき返却されるのは、最初に実行されたVisitメソッドの戻り値になります。ICustomAstVisitorはインターフェースですので、処理はすべて自分で定義しなくてはなりません(※)。ノードの再帰的探索も、必要ならもちろん自前で実装する必要があります(前回紹介した、JSON化スクリプトのような処理になるかと思います)。※ISEだとインターフェースの実装を一発で行うリファクタリング機能はないので、今回みたく実装すべきメンバがたくさんある場合は、こんな感じのひな形を作るスクリプトを使うと良いでしょう。今回はICustomAstVisitorインターフェースを実装したクラスの実例まではご紹介できませんでしたが、興味のある方は、PSScriptAnalyzerで用いられているので参考にしてみてください。まとめPowerShellのASTについてきちんと解説している記事が英語圏を含めてもあまりないようでしたので、3回に渡って、一通りの基礎知識をまとめてみました。普通にPowerShellを使っている分には、滅多に使うことはないと思いますが、たとえばPSScriptAnalyzerのカスタムルールを自分で作る場合には、ASTの知識は必須になってきますので、必要に応じて参考にしていただければ幸いです。2016の15日目です。前回はPowerShellのASTの概要を解説しました。今回は前回の補足というか応用的な内容になります。前回、スクリプトブロックからどのようなASTが生成されるのか、図で書きました。そもそもあの図を作るにあたって、ASTの構造を視覚的に把握したかったので、そのためのスクリプトを書きました。PowerShellで木構造を展開表示する方法は色々ある(※)かと思いますが、今回はJSONとして出力して、表示については他のアプリに任せることにしました。※Format-Customのデフォルトビューは意外と使えるただし、ASTオブジェクトをそのままConvertTo-Jsonコマンドレットに渡すわけにはいきません。いうのも、AST構造を再帰的に展開するには、探索の深さ(-Depth)を大きくしなければいけませんが、そうするとASTではないオブジェクトも逐一展開してしまい、現実的な時間内で終わらなくなってしまいます。そこで、ASTオブジェクトそのものをJSONにするのではなく、必要なプロパティのみ再帰的に取得したカスタムオブジェクトを生成し、それをJSONにする方針を取りました。その成果が以下のコードです。namespace節を使っているので、v5以上必須です。}}本来なら、50種以上あるAstクラスに応じてきちんと場合分けすべきなのですが、コードが長くなるだけなので、動的言語の強みを生かしてダックタイピング的な方法で下位ノードを再帰的に展開しています。Ast>>型であることを確認するのに苦労してますが、多分もっといい方法があると思います...。他はAstオブジェクトそのものか、ReadOnlyCollection<Ast>を返すだけなのでそんなに苦労はないです。Ast抽象クラスに含まれているExtent、Parentプロパティ以外で、Astを要素に含まないプロパティに関しては、ASTの葉として解釈しています。次にこのスクリプトを使って、スクリプトブロックをJSONとして出力します。ast.jsonサンプルとして用いるスクリプトブロックは、前回のものと同じです。これを先ほど書いたGet-Ast関数に渡して、結果をConvertTo-JsonでJSON化しています。この際、探索の深さを100としていますが、ネストが深いスクリプトブロックなどでは、もっと大きくする必要も出てくるかもしれません。Viewerを使って表示してみたのが、以下のスクリーンショットになります。色んなスクリプトのASTを表示して、楽しんでみてください。ASTシリーズはもう少し続きます。Visitorと静的解析のお話です。2016の11日目です。Treeの略で、日本語では「抽象構文木」といいます。コードをパーサーが構文解析した結果から、言語の意味に関係のない要素(空白等)を除外し、木構造として構築したものです。PowerShellでは3.0からASTの仕組みが取り入れられました。スクリプト実行時にはまずパーサーがスクリプトブロックからASTを生成し、コンパイラによってASTが解釈され、実行されるようになっています。ASTを直接的に扱うのはコンパイラですが、実はPowerShellではパーサーが構築したASTを、PowerShellスクリプトから扱うことができます。ASTの具体的な使い道としては、構文の静的解析が挙げられますが、その話は後でするとして、今回はまず、ASTの構成要素と構造を見ていきます。具体的には、{スクリプトブロック}.Astとして、ScriptblockオブジェクトのAstプロパティから、ScriptBlockAstオブジェクトにアクセスできます。このオブジェクトがASTのルートとなるノード(分岐点)を表します。このScriptBlockAstから、スクリプトブロック内部の構文要素が木構造として展開されていきます。式(Expression)、文(Statement)といった構文要素は、各々対応したAstクラスが対応し、木構造における分岐点を形成します。分岐点の末端の葉では、当該の構文要素を構成するデータを示すオブジェクトが格納されます。すべてのAstクラスは、Ast抽象クラス(System.Management.Automation.Language.Ast)を継承したクラスです。PowerShellでは50個程のAstクラスが存在します。各Astクラスは、抽象クラスで定義されている以下の2つのプロパティを持っています。Parent親ノードを示すAstオブジェクトを返すExtent当該のASTノードに含まれるコード文字列や、スクリプト全体から見たコード文字列の位置等の情報を持つ、IScriptExtentインターフェースを実装したクラスのオブジェクトを返すまた各Astクラスは、対象の構文要素に応じて、それぞれ異なったプロパティを持ちます。ScriptBlockAstは以下のプロパティを持ちます。}}という、二つの整数値の和を赤字で表示するというスクリプトブロックならば、以下のようなASTが構築されます。(一部分岐点、葉は省略しています。分岐点のASTクラス名は、末尾の"Ast"を省略表記しています。Redのようにします。基本的なASTの構造が頭に入っていれば、タブ補完を併用することで比較的簡単に目的のノードまで辿れますが、ASTノードの子に対し、ノード検索をかける方法もあります。$true)のように、FindAllメソッドを用います。AST編はあと何回か続く予定です。2016の1日目です。PowerShellアドベントカレンダー、今年も始まりました。みなさまのご協力の甲斐あって、過去5年間はすべて完走していますが、今回もできれば完走を目指していく感じでまいりましょう。色々な立場の方からの視点でPowerShellを俯瞰できるこのイベント、私自身も毎年楽しんでおります。去年も2015年のPowerShellをまとめる的な記事から始めたわけですが、今年も去年に負けず劣らず、大変革の年だったと思いますので、今回も1年を振り返るところからスタートしましょう。去年のPowerShell5.0登場、周辺モジュールのOSS化といった大きな動きがあってから、今年の前半は少しおとなしめ?の界隈でした(5.0のインストーラーにバグがあって一時非公開とか小騒動はありました)が、まず驚いたのがPowerShellそのものではなくて、Windowsで動くbashが登場したというトピックですね。発表があったのは今年の3月末の事です。Linux"という仕組みの上で動作するUbuntu上で動作するbashとして動作させてしまおうというものです。Windows"という名称になります。このbash、Windowsのシステムを管理するためのものではなく、Web等の開発用途を想定して提供されたものです。本来的にはPowerShellとは関係ないのですが、当初は色々と誤解が飛び交ったように思います。曰く、MSはPowerShellを捨ててやっとbashを採用した。PowerShellとは何だったのか。これらの誤解や疑問には、PowerShellの公式ブログで、bashという新たなシェルがWindowsに追加されたが、両者は並立するものだ、PowerShellはこれからも進化するよ!といった異例の公式見解が示されたりもしました。Updateで正式に利用可能となりました。5.1)のプレビュー版が登場しました。10に同梱されたものの、2016正式版までやや時間を要すこととなりました。5.0にはいくつかの機能や改良が加えられ、結局、5.1というバージョンが登場するに至ったものと思われます。Updateという大型アップデート適用で使えるようになりました。2016にも(もちろん)同梱されました。5.1ではローカルユーザーやグループを扱うコマンドレット等が(ようやく?)追加されたりもしています。Server用のPowerShellが、従来のPowerShellと分離した点でしょう。Edition"と呼ばれます。Edition"が新たに登場しました。Core上で動作します。CoreはOSS(オープンソースソフトウェア)となっています。Serverの管理に必要十分な機能を保っているのではないかと個人的には思っています。今年、PowerShell界をもっとも震撼させたニュース、それは間違いなく、今年8月に実施された、PowerShellのオープンソース化でしょう[GitHub]。Editionを含んだ全体だったので驚きました。Macが登場しました。これも一部の方、特にWindowsやMicrosoft製品を普段あまり使われない方に、割と大きなインパクトを与えていたように思います。system!"になったことの意義についてはちょっと語りつくすには時間が足り無さそうですが、敢えて現実ベースの話を先にすると、一般ユーザー(PowerShellをシステム管理に用いている管理者)にとって、すぐに世界が変化するかというとそうではないんじゃないかという気がしています。いうのも、現在のところOSS版のPowerShellのバージョンは「6.0」とされているものの、まだα版の段階で、基本機能はほぼほぼ5.1と変わらないです。OSS版が改良されても、別に今Windowsで使っているPowerShellがすぐに強くなるわけではありません(現在のところ、サイドバイサイドでインストールする)。オープンソース、マルチプラットフォーム展開を始めたといっても、それは現在の所、PowerShell本体とコアモジュールに留まっていて、コマンド数もたかだか数百個程度でしょう(もうちょっとあるかな?)。PowerShellでOS、サーバー、アプリ、インフラを管理するには、専用のコマンドが山ほど必要になってきますが、それらは今まで通り、Windowsの製品にしか含まれないものです。そのような状況で、たとえばLinuxを管理するのには自分でコマンドを作るか、普通にLinuxのコマンドを呼ぶか...あれそれって別に普通のシェルスクリプトでいいんじゃ...とか。Server上のPowerShellに反映され、両者は一本化される、はず、です、たぶん、が、それはまだアナウンスもなく、いつ、どのような形でもたらされるかは不明と云わざるを得ません。この状況はあくまで現時点の状況です。PowerShellで、WindowsもLinuxもMacも一貫したコマンド&スクリプトで管理できる日が来るかもしれませんね。現在のところは、PowerShellの謎挙動に遭遇したとき、ソースを合法的に眺めて思いを馳せることができるようになったのが大きいかと個人的には思います。腕に覚えのある方は、(ルールに従って)どしどしプルリクエストを投げて、PowerShellを自ら育てていただければな、と思います。1.0が登場してちょうど10年ということで、PowerShell10周年イベントがあったりしました。思えば遠くへ来たものですね。PowerShellを取り巻く状況は刻一刻と変化し、去年と今年ではその傾向が顕著です。2016を、どうぞよろしくお願い致します。埋め込み型のプレゼンテーションです。自動化入門」というセッションの資料を公開します。遅くなってすみません。コンピュータの舞台裏は、コンピュータの仕組み、基礎の部分から扱う勉強会ですので、私の方からは、自動化ってそもそも何がうれしいの?というところから話をさせていただきました。コンピュータの自動化手法として、プログラム言語やスクリプト言語を利用する方法がまず挙げられると思いますが、昨今は無料で使える言語がたくさんあります。その中でも、「システム管理用」を銘打つPowerShellは、システムの構成要素として重要な「ファイル」を扱うことにかけては、強力かつ簡単だと思います。いうわけで、目の前のコンピュータの自動化の第一歩として、PowerShellを触ってみるのは悪くないんじゃないかと思っています。埋め込み型のプレゼンテーションです。遅くなってすみません。新機能のご紹介」の資料を公開します。札幌は涼しくて、何もかも美味しくて良かったです。夏の関西は人間の生存に適した気候とはとても言えないので、しばらく札幌に滞在していたかったですね。5.1の正式版が利用できるようになるということで、今回、5.0に加えて5.1の新機能もご紹介することにしたためです。(スライドは5.1の新機能の部分以外は、基本的にこれまでの内容と同様です。TP5で試すことができます。2016正式リリースまでの期間がけっこう空いたことと、ラピッドリリースの方針もあって、5.1が短期間で登場することとなりました。Editionに分かれるところだと思います。Serverで動作させることを目的として作られました。Serverの管理はリモート経由でPowerShellを直接的あるいは間接的に用いて行うことになるため、当然ながら必須のコンポーネントとなります。Core上で動作します。Frameworkのサブセットで、マルチプラットフォームで動作し、OSSとして開発されています。Editionとしてこれまで通り利用可能です。5.0、5.1の新機能はMSDNでまとめられているのでそちらもご参照ください。(Preview)追伸。Managementを7月付で再受賞いたしました。おかげで今回のイベントで「関西MVP3人が集結」という触れ込みが嘘にならなくて良かったです。同じ分野でCLR/Hのスタッフでもある素敵なおひげさんも受賞されました。おめでとうございます。埋め込み型のプレゼンテーションです。勉強会(4/9)には多数の方にお越しいただき、ありがとうございました。PowerShell勉強会は今後も年2回くらいのペースで続けて行きたいと思っていますので、どうぞよろしくお願い致します。新機能と関連OSSのご紹介」のスライドを公開します。前半は以前のものとだいたい同じですが、正式版対応版にアップデートしています。今回は去年から今年にかけて、PowerShell関連ソフトウェアとしてOSS化したものを、まとめて紹介しました。以下は今回紹介したもののリストです。Chefまたデモで用いたサンプルファイルも公開します。このzipにも同梱してますが、PSScriptAnalyzerのカスタムルールはこんな感じで作ります。作り方は、ASTを受け取って、中身をチェックして、ルールに該当するならDiagnosticRecordを返すというのが基本になります。変数に半角英数字以外の文字種が含まれていると警告するカスタムルール。半角英数字以外の文字種が使われています。Test-UsingVarsWithNonAsciiCharacterついでにPesterのサンプルコードも。2つのパラメータを足し算する関数、Invoke-Additionに対するテストコードの例となります。@mutaguchiBooks書籍版。