2014/12/01

はじめに

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

次期バージョンのPowerShell 5.0について、そろそろ情報が出回ってきました。現在のところWindows Management Framework 5.0 Preview November 2014、もしくはWindows 10 Technical PreviewWindows Server Technical Previewに同梱のもので試すことができます。

v5での新機能、改善点は多岐に上ります。OneGet / PowerShellGet / クラス定義 / DSC機能増強 / ODataエンドポイントのコマンドレット化 / zipファイル / シンボリックリンク 等々。詳しくは、リリースノートが一番充実しているかと思います。日本語だとぎたぱそ氏の記事がまとまっているかと思ます。

さて、ここまで挙げた新機能や改善点は、とても順当でまっとうな進化点なのですが、v5にはちょっと異彩を放つ新機能がしれっと追加されています。それが、Auto-Generated Example-Driven Parsing です。

Auto-Generated Example-Driven Parsing とは

CSV、JSON、XMLのような既知のフォーマットではないが、何らかの法則性のあるテキストデータがあるとします。そんなテキストデータは(不幸なことに)割と世の中にあふれていますが、そのままでは(人が読む以外には)利用できないので、データとして扱うには、解析し、レコード(プロパティ:プロパティ値)として再構築する必要があります。

しかしながらフォーマットが既知のものではないため、既存のパーサーを使って解析することはできません。

従来のアプローチだと、このようなデータに対しては、まずユーザー(人間)がデータの法則性を読み取り、その法則をコンピュータに分かる表現(コードや正規表現など)に変換してやる必要がありました。

Auto-Generated Example-Driven Parsing とは、事前にユーザーがテキストデータの一部分のみを取り出し、各項目に対してプロパティ名を指示したデータ(テンプレート)を用意しておくと、元のテキストデータとユーザーが用意したテンプレートから法則性を解析し、元のテキストデータ全体を自動的にテキストデータからオブジェクトに変換してくれる機能です。

Auto-Generated Example-Driven Parsing とはもともとMicrosoft Research で研究されているFlashExtract というデータ解析手法の PowerShell コマンドレット(ConvertFrom-String)による実装になります。ConvertFrom-StringData じゃないですよ。全然別物です。これうっかりしてるとスルーしてしまいそうです。

具体例

たとえば、こんなデータがあったとします。

山内 佳乃 (やまうち よしの)
生年月日...1982/1/27 (32歳)、女性
田畑 真帆 (たばた まほ)
生年月日...1966/4/14 (48歳)、女性
三好 一樹 (みよし かずき)
生年月日...1972/7/10 (42歳)、男性
酒井 幸平 (さかい こうへい)
生年月日...1954/3/1 (60歳)、男性
藤島 恵子 (ふじしま けいこ)
生年月日...1969/5/4 (45歳)、女性
加藤 美優 (かとう みゅう)
生年月日...1986/12/8 (27歳)、女性
金谷 康文 (かなや やすふみ)
生年月日...1983/10/7 (31歳)、男性
岸本 紗季 (きしもと さき)
生年月日...1984/5/16 (30歳)、女性
永野 ケンイチ (ながの けんいち)
生年月日...1961/7/8 (53歳)、男性
小関 三郎 (こぜき さぶろう)
生年月日...1975/1/22 (39歳)、男性
山岸 光 (やまぎし ひかる)
生年月日...1939/2/13 (75歳)、女性
黒谷 恵麻 (くろたに えま)
生年月日...1949/2/13 (65歳)、女性

名前や生年月日が書かれたデータで、一応、法則性はあるようです。が、これをまともにパースしようと思うと、2行ごとに切り出して、正規表現を書いて…と、ちょっと面倒ですね。

ちなみにこのダミーデータ作成にはなんちゃって個人情報を使わせていただきました。CSVで出力した後、以下のようなスクリプトでわざわざ醜く変形しました。

Import-Csv -Encoding Default -Path dummy.cgi|%{"$($_.名前) ($($_.ふりがな))`n生年月日...$($_.誕生日) ($($_.年齢)歳)、$($_.性別)性"}|set-content -Encoding UTF8 -Path dummy.txt

さて、このテキストデータに対し、Auto-Generated Example-Driven Parsingで用いるテンプレートを書いてやりましょう。たとえば、以下のように適当に3件(ここでは3〜5個目のレコード)抜き出して、プロパティ名をつけてやります。赤字が、手動で元データに付与した文字列です。

{Name*:三好 一樹} ({Furigana:みよし かずき})
生年月日...{BirthDay:1972/7/10} ({Age:42}歳)、{Sexuality:}{Name*:酒井 幸平} ({Furigana:さかい こうへい})
生年月日...{BirthDay:1954/3/1} ({Age:60}歳)、{Sexuality:}{Name*:藤島 恵子} ({Furigana:ふじしま けいこ})
生年月日...{BirthDay:1969/5/4} ({Age:45}歳)、{Sexuality:}

みて頂ければ分かると思いますが、基本は、各データ項目に対して、{プロパティ名:データ}のように指定してやるだけです。主キーとなるデータ項目にはプロパティ名の後に「*」をつけてやります。こうやって作ったテンプレートをtemplate.txtと名前を付けて保存しましょう。

元データとテンプレートが揃ったので、あとは以下のようにしてConvertFrom-Stringコマンドレットを実行するだけです。

image

テンプレートを元に、元テキストに含まれるすべてのデータが、プロパティ値を持ったオブジェクトデータに変換されていることが分かるかと思います。これちょっとすごくないですか?

まとめ

Auto-Generated Example-Driven Parsingは個人的には、非常に面白い機能だと感じています。コンピュータに対して、「手本見せるよ、これはこう、これはこう。わかった? じゃ、あとは同じようにまとめといてね!」というのができるようになったわけで、ちょっと未来を感じました。

研究所レベルの研究成果を、製品として実装した初の例が、PowerShellだったというのも面白味を感じます。

ただ、CSVでもJSONでもXMLでもない、わけのわからない謎フォーマットで保存されたテキストデータを解析しなきゃならない事態というのは、そもそも不幸な状況であることも、また事実かと思います。

ConvertFrom-Stringは、そんな訳の分からないものを撲滅して、今度こそまともなフォーマットのデータに変換して保存するための、最終兵器のようなものかもしれません。

なお、Auto-Generated Example-Driven Parsingでは他にもプロパティに型を指定したり、部分的に正規表現を用いたり、階層構造を持つデータにも対応してたりと、かなり色々なことができるようになっています。ぜひ、v5環境を整えて、ConvertFrom-Stringを試してみてください。

さてさて、PowerShell Advent Calendar 2014、今年は参加者が少なく、完走はかなり危ぶまれますが、できるところまで行きたいですね! これをお読みのあなたの記事が読みたいです! ぜひ、ご参加いただけると幸いです。

2012/03/06

次期Windows Server OSであるWindows Server “8”のベータ版が3/1よりWindows 8 Consumer Preview版と同時に提供が開始されました。今回のリリースでは日本語版も提供されています。

それと同時にPowerShell 3.0 betaを含むWindows Management Framework (WMF) 3.0 betaの提供も始まりました。こちらは英語版のみの提供で、日本語環境にインストールするには英語言語パッケージをダウンロードし適用する必要があります。

まだ評価を初めて間もない段階ですが、きづいた点をいくつか書いてみます。

PowerShell ISEがDeveloper Preview/CTP版から大きく変わっており、コマンドを入力する「コマンドペイン」と結果が表示される「出力ペイン」が統合され、「コンソールペイン」となりました。image

こんな感じでコンソールペインはその名の通りコンソール版のPowerShellと見た目や使用感が似ている上にISEならではの機能(インテリセンスなど)が使用可能になっており、ISEはスクリプト編集のみならずインタラクティブシェルとしてpowershell.exeの代わりに使用する場合でもより便利になったと言えるでしょう。なおISEのキーワード色分け設定は細かく指定できたりします。

Server8ではActive Directory管理センターの大幅な機能増強が目に留まります。Active Directory管理センターには「Windows PowerShell 履歴」ペインが追加され、GUIで操作した履歴がそのまま、再実行可能なPowerShellスクリプトとして表示されるようになりました。

たとえばユーザーを作成する作業をGUIでやります。

image

するとその作業内容がこのように履歴ペインに表示されます。

image

履歴はまさにPowerShellスクリプトそのままなので、これを「コピー」してISEに貼りつけるともうスクリプトファイルが完成します。

image

あとは必要に応じてパラメータを変更したり、ループを設けて繰り返し処理をしたり、自由自在に再利用ができます。ここではユーザー名を”testuser2”に変えて実行してみました。最初にGUIで作ったユーザーと同じ設定を用いて複数のユーザーを作成することが簡単に行えます。ユーザー名を記載したCSVファイルを読み込んでそのユーザーを一括作成することなどもできますね(PowerShellにはImport-CsvというCSVファイルを扱うコマンドレットもあります)。

スクリプトファイルにして保存すれば何回でも実行可能ですし、PSScheduledJobモジュールを使ってタスクスケジューラに登録すれば定期実行も可能です。

履歴スクリプトのうち、どこからどこまでが「今やった作業」なのか分かりにくいこともあると思いますが、そういうときには履歴ペインの「タスクの開始」をクリックし、今からやりたい作業名をまずメモします。

image

そしてGUIで作業を行い、終わったら「タスクの終了」をクリックします。

image

作業単位でこれを繰り返します。すべてが終わったら履歴をすべてコピーしてISEに貼りつけてみます。

image

するとこのようにタスク名がコメント行として挿入され、スクリプトのどこからどこまでが、どの作業に対応しているかが明確になるわけです。

このように管理GUIがPowerShellモジュール(コマンドレット)のフロントエンド的存在となり、GUIでの操作によりPowerShellコマンドレットが実行され、その履歴はスクリプトとして再利用が可能というMicrosoftが提唱する新しい管理方式が、ついにWindows Serverの本丸的存在Active Directoryに採用されたということになります。2008R2のActive Directory管理センターも内部的にはそういう構造だったのですが、履歴をスクリプトとして取り出す方法がなかったのですが、Server8からは可能になったということになります。

これまではこの構造が完全に組み込まれていたのはExchange Serverなど限られた製品のみでしたが、今後はこちらの構造が主流になると思われます。GUIとCUIの「いいとこどり」ができるこの構造はなかなか理想的なシステムなんじゃないかと思います。

PowerShellは3.0になってますますWindows OS管理の中核として重要度が高まっており、それに答えるように様々な機能増強が行われています。今回紹介したのはその一面ですが、特にServer8においてPowerShellがいかに重要かはWindows Server "8" に関するテクニカル プレビューの各項目の記述内容の多くにPowerShellに関する記載があることでも分かるかと思います。

2011/12/02

はじめに

このたび、技術系アドベントカレンダーイベントの1つとして、PowerShell Advent Calendar 2011を企画しました。この記事はその2日目の記事となります。アドベントカレンダーについてはリンク先を参照してください。

今日のテーマはPowerShellのバックグラウンドジョブ機能の使い方についてのまとめです。

バックグラウンドジョブとは

バックグラウンドジョブ機能はその名の通り、ジョブ(具体的にはスクリプト)をバックグラウンドで非同期に実行するものです。PowerShell v2で追加された機能の一つです。インタラクティブシェルでStart-Jobコマンドレットを使用してバックグラウンドジョブ(以下、単に「ジョブ」と表記)を実行すると、新しくpowershell.exeのプロセスが起動しそのままシェルに制御が戻りユーザーは後続の処理を行うことができます。もちろんスクリプトからジョブを実行することも可能です。時間のかかる処理をバックグラウンドで走らせたり、数多くの処理を並列で実行したりするのに重宝します。

起動されたジョブは操作中のpowershell.exeとは別のジョブ用のプロセスで実行され、処理が完了すると呼び出し元でその結果をReceive-Jobコマンドレットを使って受け取ることができます。ジョブは並列して何個も同時に実行できます。なおPowerShellのジョブは1ジョブ=1プロセスです。スレッドではないので注意。

PowerShellのジョブシステムはリモート処理インフラストラクチャの上に構築されているので、たとえローカルPCでもジョブ実行するにはローカルPCをリモート用構成にしておく必要があります。詳しくはabout_Remote_Requirementsを参照のこと。

ジョブはローカルでもリモートでも走らせることができます。以下に具体的な方法を述べていきます。

ローカルコンピュータでのジョブ実行

ローカルコンピュータ上に新しくジョブを作成して開始するにはStart-Jobコマンドレットを用います。

Start-Job {ジョブとして実行したいコマンド、スクリプト}

とするとジョブを実行します。

$job=Start-Job {..}

のようにするとJobオブジェクト(System.Management.Automation.PSRemotingJob)を変数に格納してあとで利用できます。変数で受けない場合はJobオブジェクトの内容が表示されます。

存在するジョブを取得するにはGet-Jobコマンドレットを用います。

Get-Job

で現在実行中のジョブ一覧を表示します。以下に出力例を示します。

Id              Name            State      HasMoreData     Location             Command
--              ----            -----      -----------     --------             -------
1               Job1            Completed  True            localhost            "test"
3               Job3            Running    True            localhost            start-sleep -sec 120;"...

以下の表は各項目の意味です。

Id ジョブID番号
Name ジョブの名前
State

Running=実行中のジョブ

Stopped=停止したジョブ

Complete=完了したジョブ

Failed=エラーが出たジョブ

HasMoreData 返却されたデータがあるかどうか
Location ジョブが実行されているコンピュータ名
Command ジョブで実行されているコマンド、スクリプト

ジョブの終了を待つにはWait-Jobコマンドレットを用います。

Get-Job|Wait-Job

とすると実行中のジョブすべてが完了するまで待ちます。-timeoutパラメータを使うと最大待ち時間(秒)を指定できます。

Get-Job|Wait-Job -any

とすると実行中のいずれかのジョブが完了するまで待ちます。正確には「対象のジョブが一つ以上完了するまで待つ」という効果なので、完了済みのジョブが1つ以上ある場合に新たにジョブを追加した場合などは想定の動作になりません。あらかじめRemove-Jobで完了済みのジョブを削除するか、Where-ObjectコマンドレットでRunningのみ対象にするようフィルタをかけるかしてください。

ジョブを中止するにはStop-Jobコマンドレットを用います。

Get-Job -id 1|Stop-Job

とするとジョブIDが1のジョブを中止します。

$jobにJobオブジェクトが格納されている場合は

$job|Stop-Job

でもOKです。

ジョブを削除するにはRemove-Jobコマンドレットを用います。

Get-Job|where {$_.state -eq "Completed" -or $_.state -eq "Stopped"}|Remove-Job

とすると完了済みと中止したジョブを削除します。実行中のジョブは削除できませんが-forceパラメータを使って強制削除することは可能です。

ジョブの実行結果データを取得するにはReceive-Jobコマンドレットを用います。

Get-Job|Receive-Job

とすると完了済みのジョブのうち、結果を返却しているもの(HasMoreDataがTrueのジョブ)があればその結果を表示します。-keepパラメータをつければ結果データを保持しますが付けてない場合は参照後破棄します。

*-Job系のコマンドレットの多くはJobオブジェクトを返却するので、パイプラインでどんどん繋げていけます。

Get-Job|Wait-Job -timeout 10|Receive-Job

のように。

ジョブの基本的な使い方に関して詳しくはabout_jobsを参照してください。

イベントサブスクライブ

PowerShell 2.0では.NET Frameworkのオブジェクトのイベントをサブスクライブすることができます。すなわちイベントハンドラを記述することができます。このイベントサブスクライブ機能もジョブ機能を元に構築されています。

たとえばTimerオブジェクトのElapsedイベントをサブスクライブし、タイマーの実行間隔(ここでは1秒)ごとにtest.txtファイルに乱数を追記していくサンプルは次のようになります。

$timer=new-object System.Timers.Timer
$timer.Interval=1000
Register-ObjectEvent -EventName Elapsed -SourceIdentifier test -Action {get-random|add-content c:\users\daisuke\test.txt} -InputObject $timer
$timer.Enabled=$true

Register-ObjectEventの結果、新しくジョブが生成しそのJobオブジェクトが返却されます。このジョブは-EventNameパラメータで指定したイベントが発生するたび、-Actionパラメータで指定したスクリプトブロックを実行します。

なお、イベントサブスクライブを解除するには

Unregister-Event test

のように-SourceIdentifierパラメータで指定した値を指定してUnregister-Eventコマンドレットを実行することで可能です。サブスクライブを解除してもジョブ自体は削除されない(StateがStoppedになるだけ)ので、必要であればRemove-Jobで削除します。

なお.NETオブジェクトの他にPowerShellスクリプトのカスタムイベント(Register-EngineEvent)、WMIオブジェクトのイベント(Register-WmiEvent)をサブスクライブすることもできます。これらのコマンドレットも同様にイベント発生時の処理をジョブとして登録します。詳しくは各コマンドレットのヘルプを参照してください。

リモートコンピュータでのジョブ実行

最初に述べたとおりPowerShellのジョブ機能はリモートインフラストラクチャの上に構築されています。よってローカルのみならずリモートコンピュータに対してジョブを実行することができます。もちろんリモートコンピュータにもリモート構成されていることが条件です。

基本はInvoke-Commandコマンドレットを用い、

$job=Invoke-Command -ComputerName リモートコンピュータ名 {リモートで実行するコマンド、スクリプト} -asjob

となります。これで{}内の処理がリモートコンピュータ上のPowerShellインスタンスで実行されます。-asJobパラメータをつけることでジョブとして(ローカルPCから見て)非同期に処理できますが、-asJobパラメータを省略すると同期的に実行されます。この場合ジョブは作成されず、リモートでの処理が終了するまでローカル側は待機することになります。

リモートコンピュータに接続するための資格情報を別途入力する必要がある場合は-credentialパラメータを使用します。

Invoke-Command -ComputerName リモートコンピュータ名 {リモートで実行するコマンド、スクリプト} -asjob -credential ユーザー名

とするとパスワードを入力するダイアログが表示されます。なお、スクリプトで動かすときなどあらかじめ入力したパスワードを指定したい場合の方法は以前書きました

同じコマンドを複数のリモートPCで同時実行することも可能で、その場合は-computerNameパラメータにリモートコンピュータ名の配列を指定します(「,」区切り)。この場合ローカルPCで見えるジョブとしては1つですが、そのジョブにリモートコンピュータの数だけ子ジョブ(ChildJobs)が作成されています。

このように子ジョブが複数ある場合にReceive-Jobするときは

$job|Receive-Job -location リモートコンピュータ名

あるいは

$job.ChildJobs

として表示される子ジョブの名前(Name)を調べ、

Receive-Job -name 子ジョブの名前

とすることでリモートコンピュータごとに結果を取得できます。

すべての結果をまとめて取得するなら

Receive-Job $job

とします。

$job|Receive-Jobはなぜか駄目なようです。

固定セッションを用いたリモーティング

同じリモートPCに対して何度もコマンドを実行させたい場合、毎回リモートコンピュータ名を指定してセッションを張るのは非効率的なので、リモートセッションを確立したあとその固定セッションを何度も使用する方法が用意されています。新しく固定セッションを確立するにはNew-PSSessionコマンドレットを用い、

$session=New-PSSession リモートコンピュータ名

とすると固定セッションが確立され、$session変数にそのセッションオブジェクトが格納されます。あとは

Invoke-Command $session {リモートで実行するコマンド、スクリプト} -asjob

とすればそのたびにそのセッションを用いてリモートでコマンドを実行できるようになります。

ここまでの説明はリモートコンピュータでしてきましたが、ローカルコンピュータに対して固定セッションを張ることも可能です。

さらに、Enter-PSSessionコマンドレットを用いると作成したセッションに入ってリモートコンピュータ上のPowerShellを対話実行することも可能です。

Enter-PSSession $session

とすると、プロンプトが

PS カレントディレクトリ>

から

[リモートコンピュータ名]: PS カレントディレクトリ> 

に変化し、以降リモートのPowerShellをローカルPCから対話実行できます。

なおこの状態から抜けるにはexitもしくはExit-PSSessionと入力して実行します。

ジョブ実行できるそのほかのコマンドレット

これまで述べたコマンドレット以外にも、いくつかのコマンドレットはジョブ実行(ローカルorリモート)することができます。ジョブ実行するには-asJobパラメータを使用します。以下にv2の段階で-asJobパラメータが定義されているそのほかのコマンドレットを示します。

これらのコマンドレットはコマンドレット自体にジョブ実行機能がついているので、単独で実行するだけならStart-JobやInvoke-Commandを用いる必要がありません。v2ではWMIを扱うコマンドレットにのみ-asJobパラメータが存在するようです(ここに挙げたコマンドレットはすべてWMIの機能を呼び出すもの)。なお、-asJobパラメータが使用できるコマンドレットの一覧を取得するのに、fsugiyamaさんの1日目の記事の問15のスクリプトを使用させていただきました。

おわりに

PowerShell Advent Calendar 2011二日目は、PowerShellのバックグラウンドジョブ機能概要についてまとめてみました。実はバックグランドジョブ機能のTipsを書こうと思ってその前ふりとして書き始めたのですが、これだけでかなりの量になってしまったので概要だけ一記事としてまとめることにしました。おそらくPSアドベントカレンダーに私はあと何回か登場することになりそうですので、Tips編はその際に書こうと思います。

さて、明日三日目は@jsakamotoさんのご登場ですね。よろしくお願いします!

そして参加者はまだまだ募集中ですよ!→PowerShell Advent Calendar 2011


Copyright © 2005-2016 Daisuke Mutaguchi All rights reserved

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

Awards

Books

Twitter