2011/12/19
PowerShell 3.0で追加されるバックグラウンドジョブ関係の新機能 [PS Advent Calendar '11]
はじめに
PowerShell Advent Calendar 2011の19日目の記事、そしてこれが私の記事では3回目となります。今回も前々回、前回からの引き続きでバックグラウンドジョブについての話題です。前回までは現行バージョンであるPowerShell 2.0におけるバックグラウンドジョブの機能の使い方を解説してきましたが、今回はPowerShellの次期バージョンである3.0に追加される予定の機能のうち、ジョブ関係のものをピックアップしてみます。現在PowerShell 3.0を含むWindows Management Framework(WMF)3.0のCTP2が公開されています。またWindows 8 Developer Preview / Windows Server 8 Developer PreviewにはWMF3.0 CTP1相当のPowerShell 3.0が含まれています。
注意:本記事で取り上げた内容は製品のプレビュー版をもとに記述しています。そのためリリース版では内容が一致しない可能性があることをご承知おきください。
using:ラベル
前回、ジョブに値を渡す方法について解説しましたが、-argumentListに引数として渡すというのは正直めんどうです。呼び出し元のグローバル変数を直接ジョブ側から参照したいですよね。そこでPowerShell v3では新たに変数に付けるusing:ラベルというのが追加されました。このラベルをジョブのスクリプトブロック内で使うと、呼び出し元の変数を参照することができます。具体例。
$test="PowerShell 3.0" Start-Job {$using:test}|Wait-Job|Receive-Job
とすると、「PowerShell 3.0」と表示され、たしかにジョブのスクリプトブロックから呼び出し元の変数を参照できていることがわかります。これは便利ですね。ただし残念ながらこの方法を使ってもスクリプトブロックをジョブに渡すことはできないようです。相変わらず文字列にキャストされてしまいました。
Receive-Jobコマンドレットの変更点
前々回に、Invoke-Command -asJobで複数リモートコンピュータに対してジョブを走らせた場合、そのジョブに対して$job|Receive-Jobがなぜか機能しない、と書きましたがこの問題が解決されています。そもそもなんでこの問題が発生していたのか、面白いのでちょっと解説します。
実はReceive-Jobコマンドレットの-locationパラメータに「パイプライン入力を許可する true (ByPropertyName)」フラグがついていたのが原因でした。複数コンピュータに対して実行したジョブは子ジョブを複数持ちますが、親ジョブ自体は配列ではありません。そしてそのLocationプロパティには子ジョブが実行されているコンピュータ名が"remote01,remote02,remote03"のようなカンマ区切りの文字列として格納されています。よってこのジョブオブジェクトをパイプラインを通じてReceive-Jobコマンドレットに渡すと、ValueFromPipelineByPropertyName属性が付いている-locationパラメータにジョブオブジェクトのLocationプロパティの値が渡されますが、その値はカンマ区切りの文字列なので正しく解釈されず、結果として期待の動作をしなかったわけです。
v3ではReceive-Job -locationのValueFromPipelineByPropertyName属性が取り除かれ、問題なく動作するようになりました。
他の変更点としてはReceive-Jobにジョブが完了するまで待つための-waitパラメータが追加されました。が、$job|Wait-Job|Receive-Jobと違いが分からないかも…。
Get-Jobコマンドレットの変更点
Get-Jobに-filterパラメータが追加されました。連想配列でジョブにフィルタをかけられるものです。
Get-Job -filter @{State="Completed";Location="localhost"}
where-objectを使わずともフィルタできるので便利、かも。しかし個人的には-filterパラメータはいろんなコマンドレットで定義されているものの、使い方がそれぞれ異なるのがとてもとてもイヤです。まず覚えられないのでヘルプを引くところから始まっちゃいますので。パフォーマンスの関係上、Where-Objectを使うよりコマンドレット内部でフィルタしたほうが速くなるというのはわかるのですが、もう少しフィルタ方式に統一性を持たせられなかったんだろうかとか思いますね。
Get-Jobにはほかに-afterと-beforeというパラメータが追加されています。これは後述するPSScheduledJobの完了時刻をDateTimeで範囲指定し、フィルタするものです。
PowerShell Workflow
PowerShell3.0というかWMF3.0のおそらく目玉機能の一つがPowerShell Workflowです。文字通り、PowerShellでワークフローが記述できるようになります。
Workflowは関数の一種なのですが、長時間を要するタスクやリモート実行や並列実行などで使うことを主目的としているようです。functionキーワードの代わりにworkflowキーワードでワークフローを定義すると、自動的に実行対象コンピュータ名や資格情報といったパラメータが複数定義されるので、これらのパラメータを特に定義なしで利用することができます。またworkflow内ではparallelブロックを定義でき、その中に記述された各行は並列に実行されます。またfor/foreachステートメントで-parallelパラメータが利用可能になり、繰り返し処理やコレクションの列挙を並列して行うことができるようになります。
自動定義されるパラメータに-asJobがあり、これを利用するとworkflowをジョブとして実行できます。このジョブは通常のジョブとは違い、新たに追加されたSuspend-JobコマンドレットとResume-Jobを使うことによって、ジョブの一時中断と再開ができます。このジョブの中断と再開は、リモートコンピュータ上でワークフローを走らせてるときでも可能ですし、中断後リモートセッションが切断されたあとに再開することもできますし、リモートコンピュータがシャットダウンしても再起動後にジョブを再開することまでできてしまいます。これらはWMFにおけるリモート基盤を支えているWinRMの最新バージョン、WinRM3.0が実現している機能です。このようにセッションを再接続してもタスクを継続できるような接続をrobust(堅牢な), resilient(弾力性のある、障害から容易に回復する) connectionと称しているようです。
PowerShell WorkflowはWindows Workflow Foundation(WF)と密接な関係があり、WFのデザイナで作ったxamlをPS Workflowに変換したり(逆もできる?)、Invoke-Expressionでxamlを実行したりできるらしいです。WF側でもPowerShellの多くの機能がアクティビティとして使用できたりして、WFとPowerShellがWMFというシステム管理フレームワークの主要なパーツとして密に連携していくようです。このあたりの話はWFの専門家であるAhfさんがPSアドベントカレンダーの23日目にしてくださる予定なので、楽しみですね!
なおPS Workflowは従来のPSスクリプトとは異なった利用状況を想定しているため、あるいはWFの機能と合わせるため、PSスクリプトではできるのにPS Workflowではできないことがとてもたくさんあります。forの中でbreakやcontinueステートメントが使えないとかStart-Sleepは-Secondパラメータしか指定できない(ミリ秒単位でスリープかけられない)とか色々あります。そのうちPS WorkflowとPSスクリプトの違いというドキュメントが公開されるんじゃないかと思います。
ちなみにWinRM3.0のおかげでワークフローではない通常のリモートジョブでも、New-PSSessionで作成したセッションの中でジョブを実行した場合、そのジョブが動作しているコンピュータへのセッションを切断(Disconnect-PSSession)したあと、セッションに再接続(Connect-PSSessionやReceive-PSSession)すればジョブの結果を取得したりすることができます。またセッションを作製したインスタンス(powershell.exe)でそのセッションを切断すると、それ以降は別のインスタンスやコンピュータからそのセッションにConnect-PSSessionで接続することができます。
ScheduledTasksモジュール
PowerShell3.0が含まれる次期Windowsでは大量のモジュールが追加され、それらのモジュールに含まれるコマンドレットの総数はWindows 8でも2000を超える膨大な量になります。これはWindows 8やWindows Server 8では従来のコマンドプロンプトから実行するコンソールexeコマンドのほとんどすべてをPowerShellコマンドレットに置き換える措置のためです。もちろん従来のコマンドは互換性のために残されますが、netsh.exeなど一部のコマンドではPowerShellへの移行を促すメッセージが表示されたりするようになるようです。参考:Window 8の機能の概要 − @IT
ScheduledTasksモジュールというタスクスケジューラを扱うモジュールもWindows 8 / Windows Server 8に新しく追加されるモジュールの一つで、schtasks.exeを置き換えるものとなります。これまでPowerShellでタスクスケジューラを扱うにはschtasks.exeを使うか、WMIのWin32_ScheduledJobを使う必要があり面倒でしたが、このモジュールに含まれるコマンドレットを用いるとそれが容易に行えるようになります。たとえば「notepad.exeを毎日朝10:00に起動する。バッテリ駆動のときでも実行」というタスクを「test」という名前で登録するには、
$action = New-ScheduledTaskAction -Execute "notepad.exe" $trigger = New-ScheduledTaskTrigger -At "10AM" -Daily $setting = New-ScheduledTaskSettings -AllowStartIfOnBatteries New-ScheduledTask -action $action -trigger $trigger -setting $setting|Register-ScheduledTask -TaskName test
とすれば可能であるはずです。実はServer 8 Developer Preview版ではこのコードは機能しません。タスクのトリガを作成するNew-ScheduledTaskTriggerコマンドレットが正しいオブジェクトを作ってくれないのです。これは将来のバージョンできっと修正されるかと思います。ただトリガを定義する部分をはずせば(あんまり意味はないですが)このコードは動作するので、やり方はたぶんあってると思います。
Register-ScheduledTaskコマンドレットには-asJobパラメータがあり、タスクスケジューラへの登録をジョブとしてバックグラウンドで行うことができます。ScheduledTasksモジュールはWMIを利用してタスクスケジューラを操作するので、ほかのWMI関係のコマンドレットと同様ですね。
なおScheduledTasksモジュールはデフォルトでは読み込まれていないので、使用するには本来Import-Moduleコマンドレットを使用しなければならないところですが、PowerShell3.0のCmdlet Discoveryという機能によりImport-Moduleは実行しなくてもScheduledTasksモジュールに含まれるコマンドレットを利用することができます。Cmdlet Discoveryとは現在読み込まれていて実行可能なコマンドレットの中にない、未知のコマンドレットを実行しようとしたとき、Modulesフォルダに存在するモジュールから同名のコマンドレットが定義されているものを探し出し、発見できたらそのモジュールを読み込んだうえでコマンドレットを実行するという優れた機能です。初回だけモジュールの検索とロードの手順が実行されるので待たされますが、一度Cmdlet Discoveryによってモジュールがシェルに読み込まれればあとは快適にコマンドレットを実行できるようになります。
PSScheduledJobモジュール
ScheduledTasksモジュールは-asJobパラメータが定義されているくらいで実はそれほどPowerShellのジョブとは関係ないのですが、ScheduledTasksモジュールが内包しているPSScheduledJobモジュールはPowerShellのジョブ機能と大いに関係があります。
従来PowerShellスクリプトをタスクスケジューラに登録するにはコマンドラインに"powershell.exe"を、引数に"-file hoge.ps1"を指定して、みたいなまわりくどいことをする必要がありました。しかし新しく追加されるPSScheduledJobモジュールに含まれるコマンドレット群はこの問題を解消します。PowerShellスクリプト(.ps1)あるいはスクリプトブロックをPSScheduledJobとして直接タスクスケジューラに登録できるようになり、PowerShellとタスクスケジューラのシームレスな連携を実現します。こちらはWindows 8/Server 8に付属のモジュールではなく、PowerShell 3.0に付属のモジュールなので、Win7などでも使用可能になる予定です。
使用例を見ていきましょう。
$triggers = @() $triggers += New-JobTrigger -at "2012/01/01 11:11:10" -Once $triggers += New-JobTrigger -at "10:00" -Daily $sb = { "This is Scheduled Job." Get-Date } Register-ScheduledJob -ScriptBlock $sb -Trigger $triggers -Name ScheduledJobTest1
まずNew-JobTriggerコマンドレットによってトリガー(具体的には実行時刻など)を定義します。ここでは決められた時刻に1回実行するものと、毎日同じ時刻に実行するものの2つを定義してみました。そしてこれらの時刻に実行したい内容をスクリプトブロックに記述し、これらをRegister-ScheduledJobコマンドレットで登録してやります。
するとこのスクリプトブロックはタスクスケジューラに登録され、指定時刻になると指定したスクリプトブロックの内容が実行されます。このタスクは「タスクスケジューラ― ライブラリ\Microsoft\Windows\PowerShell\ScheduledJobs」に登録されています。
このタスクのアクションは具体的には次のようになっています。
powershell.exe -NoLogo -NonInteractive -WindowStyle Hidden -Command "Import-Module PSScheduledJob; Start-Job -DefinitionName 'ScheduledJobTest2' -DefinitionPath 'C:\Users\Administrator\AppData\Local\WindowsPowerShell\ScheduledJobs' -WriteToStore | Wait-Job"
これによると、指定時刻に実際にタスクスケジューラによって実行されるのはpowershell.exeであり、Start-Jobコマンドレットを使って登録したスケジュールをPowerShellのジョブとして実行していることがわかります。Start-Jobコマンドレットの-DefinitionNameパラメータなどはPSScheduledJobのために追加されたもので、これによりRegister-ScheduledJobが出力したPSScheduledJob定義をファイルから読み込んでジョブとして実行できるようになっています。PSScheduledJob定義とジョブの出力は-DefinitionPathで指定されているフォルダの下にxmlファイルとして保存されているので興味がある方は覗いてみるといいかもしれません。
さて、スケジュールしたジョブの実行結果はどうやって受け取ればいいのでしょうか。実はこれはすごく簡単で、PSScheduledJob(ここではScheduledJobTest1という名前で定義しました)がタスクスケジューラによって一度以上実行された後は、
$job=Get-Job -name ScheduledJobTest1
とすることでJobオブジェクトとして取得することができるようになります。あとは通常のジョブと同じ取り扱いができるので、
$job|Receive-Job
などで実行結果を取得できます。
ちなみにPSScheduledJobはそれを定義したインスタンス以外でも参照することができます。具体的にはpowershell.exeでジョブをスケジューリングして終了→また別のpowershell.exeを立ち上げてimport-module PSScheduledJobしたあとGet-Job|Receive-JobしてPSScheduledJobの結果を参照、みたいなことができます。
ここで紹介した一連の操作ではスクリプトブロックをPSScheduledJobにしましたが、Register-ScheduledJobコマンドレットの-FilePathパラメータを用いれば.ps1ファイルをPSScheduledJobとして登録することも可能です。
現行バージョンのPowerShellはとにかく起動が遅いため、タスクスケジューラにスクリプトを登録しても実行が始まるまで何十秒も待たされるなどはざらでしたが、PSv3は起動がずいぶん速くなり、スペックや状況にもよるとは思いますがpowershell.exeの起動後ほんの数秒でスクリプトが走り始めます。この速度のおかげもあってPSScheduledJobはきっととても有効に機能するんじゃないかと思います。
おわりに
今回はPowerShell 3.0で増強されるバックグラウンドジョブ関係の機能をまとめてみました。これらの新機能のおかげで、時間のかかる処理や定期実行する処理を扱うのが飛躍的にやりやすくなりそうです。PowerShell 3.0で追加される機能は他にもたくさんあって、このブログでもいつか全部紹介したいと思ってるのですが、今回取り上げたジョブ関係はその中でもかなり重要な機能増加を多く含んでいると言えるでしょう。PowerShell 3.0やWindows 8/Server 8のリリースに備えてジョブ関係から予習しておくのは悪くないと思いますよ。
なんか25日のアドベントカレンダーのうち3回もバックグラウンドジョブネタをやって、PSアドベントカレンダーというより私だけ一人でPSジョブアドベントカレンダーをやってる感じでちょっと申し訳ないんですが、どうか許してください。そして前回は今回で終了するって言ってたんですが、実はまだジョブ関係の小ネタが残ってるので最終日25日にさせてください。では今日のところはこのへんで。明日はwaritohutsuさんの登場です。よろしくお願いします。
2011/05/16
連想配列を変換してユーザー定義オブジェクトを簡単に作成する
昨日の記事で取り上げたJavaScriptSerializerを用いると、連想配列から楽にJSONを作成できることが分かったのですが、この記事を書いていて思ったのは、「PowerShellの連想配列って意外に使えるな」という点でした。
現在のところ、PowerShellは独自のクラスを記述する方法がありません(Add-Typeコマンドレットを用いてC#などでクラスを書いて利用することはできますが)。Add-Memberコマンドレットを用いると、既存のオブジェクトに対し、任意のプロパティやPowerShellスクリプトで記述したメソッドを追加することはできます。素のオブジェクトであるPSObjectをNew-Objectして作ったオブジェクトでもこれは可能なので、一応ユーザー定義オブジェクトを作ることは可能です。ですが、Add-Memberコマンドレットを使うのはちょっとめんどくさいです。
「Windows PowerShellインアクション」ではAdd-Memberを使いPowerShellの関数を駆使してクラス定義構文のようなものを実装した例はありますが、いささか大仰な感は否めません。
しかし連想配列をユーザー定義オブジェクト代わりに使うと、簡単にできますしそこそこ便利に使えます。
連想配列をオブジェクトの代わりにすることのメリットとデメリット
連想配列をオブジェクトの代わりにすることのメリットは以下の三点があるかと思います。
- 簡単な記述(連想配列のリテラル)でオブジェクトが作成できる
PowerShellの連想配列リテラル@{}を使うことで、簡単に記述できます。またそれを配列化するのも@()を使うと容易です。$pcItems= @( @{ code=25; name="ハードディスク2TB"; price=7000; }, @{ code=56; name="メモリ8GB"; price=8000; } )
- ドット演算子で値の参照、設定ができる
PowerShellの連想配列は「連想配列[キー名]」のほかに、「連想配列.キー名」でもアクセスできる。Write-Host $pcItems[1].name # 値の参照 $pcItems[1].name = "test" # 値の設定 $pcItems[0].maker = "Seagate" # 要素の追加
- 連想配列の配列に対してWhere-Objectコマンドレットでフィルタをかけることができる
これは2とも関係しているのですが、通常のオブジェクトと同様にWhere-Objectコマンドレットでのフィルタ、ForEach-Objectコマンドレットでの列挙が可能です。$pcItems|?{$_.price -gt 7000}|%{Write-Host $_.name}
このようにメリットはあるのですが、本物のオブジェクトではないのでそれに起因するデメリットがいくつかあります。
- 要素(プロパティ)をいくらでも自由に追加できてしまう
これはメリットではあるのですが、デメリットでもある点です。後述するデメリットのせいで、同じキーをもつ連想配列の配列を作ったつもりでも、どれかのキー(プロパティ名)を間違えていた場合、それを検出するのが困難です。 - メソッドがうまく記述できない
連想配列要素にスクリプトブロックを指定し、&演算子で実行することでメソッド的なことはできます。しかしこのスクリプトブロック内では$thisが使えず、オブジェクトのプロパティにアクセスすることができないのでいまいちです。$pcItem= @{ name="ハードディスク2TB"; price=7000; getPrice={Write-Host $this.price}; } &$pcItem.getPrice # 何も表示されない。$thisが使えない # getPrice={Write-Host $pcItem.price}ならOKだが…
- Get-Member、Format-List、Format-Tableなどが使えない
これらのコマンドレットはあくまで連想配列オブジェクト(Hashtable)に対して行われるので、意図した結果になりません。たとえば$pcItems|Format-Listした場合、Name : name Value : ハードディスク2TB Name : code Value : 25 Name : price Value : 7000 Name : name Value : メモリ8GB Name : code Value : 56 Name : price Value : 8000
こんな表示になってしまいます。
連想配列をユーザー定義オブジェクトに変換する関数ConvertTo-PSObject
このように、連想配列の記述のお手軽さは捨てがたいものの、いくつかの問題点もあるのが現実です。そこで連想配列のお手軽さを生かしつつ、ユーザー定義オブジェクトの利便性も取るにはどうすればいいか考えました。結論は、「連想配列を変換してユーザー定義オブジェクトにする関数を書く」というものでした。それが以下になります。
#requires -version 2 function ConvertTo-PSObject { param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [System.Collections.Hashtable[]]$hash, [switch]$recurse ) process { foreach($hashElem in $hash) { $ret = New-Object PSObject foreach($key in $hashElem.keys) { if($hashElem[$key] -as [System.Collections.Hashtable[]] -and $recurse) { $ret|Add-Member -MemberType "NoteProperty" -Name $key -Value (ConvertTo-PSObject $hashElem[$key] -recurse) } elseif($hashElem[$key] -is [scriptblock]) { $ret|Add-Member -MemberType "ScriptMethod" -Name $key -Value $hashElem[$key] } else { $ret|Add-Member -MemberType "NoteProperty" -Name $key -Value $hashElem[$key] } } $ret } } }
ご覧のようにコード的には割にシンプルなものが出来ました。連想配列またはその配列をパラメータにとり、またはパイプラインから渡し、連想配列要素をプロパティまたはメソッドに変換してPSObjectにAdd-Memberしてるだけです。-recurseパラメータを付けると連想配列内に連想配列がある場合に再帰的にすべてPSObjectに変換します。
それでは実際の使用例を挙げます。
# 一番単純な例。パラメータに連想配列を渡すとPSObjectに変換する。 $book = ConvertTo-PSObject @{name="Windows PowerShell ポケットリファレンス";page=300;price=2000} Write-Host $book.name # 「Windows PowerShell ポケットリファレンス」と表示 $book.name="test" # プロパティに値をセットする Write-Host $book.name # 「test」と表示 #$book.size="A5" # 存在しないプロパティに値を代入しようとするとエラーになる # 連想配列をコードで組み立てていく例。 $mutaHash=@{} # 空の連想配列を作る $mutaHash.name="mutaguchi" # キーと値を追加 $mutaHash.age=32 $mutaHash.introduce={Write-Host ("私の名前は" + $this.name + "です。")} # スクリプトブロックを追加 $mutaHash.speak={Write-Host ($args[0])} # パラメータを取るスクリプトブロックを追加 $muta = $mutaHash|ConvertTo-PSObject # 連想配列はパイプラインで渡すことができる $muta.introduce() # 「私の名前はmutaguchiです。」と表示 $muta.speak("こんにちは。") # 「こんにちは。」と表示 # 連想配列の配列→PSObjectの配列に変換 $stationeryHashes=@() $stationeryHashes+=@{name="鉛筆";price=100} $stationeryHashes+=@{name="消しゴム";price=50} $stationeryHashes+=@{name="コピー用紙";price=500} $stationeryHashes+=@{name="万年筆";price=30000} $stationeries = ConvertTo-PSObject $stationeryHashes # "200円以上の文具を列挙" $stationeries|?{$_.price -ge 200}|%{Write-Host $_.name} # 「コピー用紙」と「万年筆」が表示 # 連想配列の配列をリテラルで一気に記述する $getPrice={Write-Host $this.price} # 共通のメソッドを定義 $pcItems= @( @{ code=25; name="ハードディスク2TB"; price=7000; getPrice=$getPrice }, @{ code=56; name="メモリ8GB"; price=8000; getPrice=$getPrice }, @{ code=137; name="23インチ液晶ディスプレイ"; price=35000; getPrice=$getPrice } )|ConvertTo-PSObject $pcItems[1].getPrice() # 「8000」と表示 $pcItems|Format-List <# 表示: name : ハードディスク2TB code : 25 price : 7000 name : メモリ8GB code : 56 price : 8000 name : 23インチ液晶ディスプレイ code : 137 price : 35000 #> # 連想配列の中に連想配列を含めたもの→PSObjectをプロパティの値に持つPSObject $blog= @{ utl="http://winscript.jp/powershell/"; title="PowerShell Scripting Weblog"; date=[datetime]"2011/05/16 00:25:31"; keywords=@("スクリプト","PowerShell","WSH"); # 配列を含めることもできる author=@{name="mutaguchi";age=32;speak={Write-Host "ようこそ私のブログへ"}} # 連想配列を含める }|ConvertTo-PSObject -recurse # -recurseパラメータを指定すると再帰的にすべての連想配列をPSObjectに変換する $blog.author.speak() # 「ようこそ私のブログへ」と表示 Write-Host $blog.keywords[1] # 「PowerShell」と表示 # ※配列要素に連想配列以外の値が含まれている場合は展開しない
このように簡単な関数一つで、連想配列にあった問題点をすべて解消しつつ簡単な記述で独自のオブジェクトを記述できるようになりました。おそらくかなり便利だと思いますのでぜひ使ってみてください。
余談:ScriptPropertyを使う場合
余談ですが、今回使用したNotePropertyはプロパティに代入できる型を指定したり、リードオンリーなプロパティを作ったりすることができません。そういうのを作りたい場合はScriptPropertyを使います。Add-Memberコマンドレットの-valueパラメータにゲッターを、-secondValueパラメータにセッターをそれぞれスクリプトブロックで記述します。
しかしこいつはあまりいけてないです。これらのスクリプトブロック内で参照するフィールドを別途Add-MemberでNotePropertyを使って作成する必要があるのですが、これをprivateにすることができません。よってGet-Memberでもばっちり表示されてしまいますし、フィールドを直接書き換えたりもできてしまいます。
また今回のように連想配列をPSObjectに変換する場合はprivateフィールド名も自動生成する必要があるのですが、それをScriptProperty内のゲッター、セッターから取得する方法がなく、たぶんInvoke-Expressionを使うしかありません。
これらを踏まえて元の連想配列要素の値の型を引き継ぎ、それ以外の型を代入できないようにしたScriptPropertyバージョンも一応書いてみました。ConvertTo-PSObject関数のelse句の部分を以下に置き換えます。
#$ret|Add-Member -MemberType "NoteProperty" -Name ("_" +$key) -Value $hash[$key] "`$ret|Add-Member -MemberType ScriptProperty -Name $key -Value {[" + $hash[$key].gettype().fullname + "]`$this._" + $key + "} -SecondValue {`$this._" + $key + "=[" + $hash[$key].gettype().fullname + "]`$args[0]}"|iex
まあこれはいまいちなんで参考程度に。
2012/08/23追記
この記事を書いた時は知らなかったのですが、実は単にNotePropertyだけを持つユーザー定義オブジェクトを作成するのであれば、もっと簡単な方法があります。
# PowerShell 1.0 $o=New-Object PSObject|Add-Member noteproperty Code 137 -pass|Add-Member noteproperty Name 23インチ液晶ディスプレイ -pass # PowerShell 2.0 $o=New-Object PSObject -Property @{Code=137;Name="23インチ液晶ディスプレイ"} # PowerShell 3.0 $o=[pscustomobject]@{Code=137;Name="23インチ液晶ディスプレイ"}
3つのコードはほぼ等価です。PowerShell 2.0と3.0では連想配列リテラルを用いて簡単にカスタムオブジェクトを作れるようになりました。
元記事:http://blogs.wankuma.com/mutaguchi/archive/2011/05/16/199086.aspx2007/11/27
(Get-UICulture) -eq (Get-Culture)
True
どっちか要らない子なんじゃ・・・違いがわからないよー
これだけではなんなのでミクシィから適当にコピペ
- ----------------------------------------------------------------------------------------------------
-
- [powershell]new-service何のために
- 2007年11月26日19:04
あるのかよくわからんー
新しくサービスを登録するっていうんだけど、そういうのってインストーラーの仕事じゃ・・・
おまけにRemove-Serviceコマンドレットがないから作っても削除できないw
sc.exe delete hoge
としないといけない。
sc delete hogeだとSet-Contentのエイリアスが動いちゃうw
なんかすげー危ないコマンドレットな気がするよ。
VistaにはWin32_LogicalMemoryConfigurationないんだ
http://msdn2.microsoft.com/en-us/library/aa394181.aspx
Windows XP and Windows Server 2003: This class is no longer supported. Use the Win32_OperatingSystem class instead.
ほう
http://www.anchorsystems.co.jp/anchor/ashp/netmon/faq.html
ファイヤウォールが WMI 呼び出しをブロックしてしまうためです。 Windows 2003 SP1 と Windows XP では、デフォルトでファイヤウォールが ON になっています。ファイヤウォールに WMI 呼び出しを通過させるようにするには、以下のスクリプトを実行してください。
Set objFirewall = CreateObject("HNetCfg.FwMgr")
Set objPolicy = objFirewall.LocalPolicy.CurrentProfile
Set objAdminSettings = objPolicy.RemoteAdminSettings
objAdminSettings.Enabled = TRUE
これで WMI 呼び出しが許可されます。
ファイアウォール嫌いー
リモートでGet-WMIObjectするときにひつよう
- [PowerShell]Get-Serviceしょぼすぎ
- 2007年11月26日00:55
Get-Serviceの戻り値が.NETのSystem.ServiceProcess.ServiceControllerなんすけど、Descriptionプロパティとかないねんな。
でもSet-ServiceでDescriptionを設定できたりする。どうやってちゃんと設定できたかを確認するかはget-wmiobject
win32_serviceで調べるらしいwなんだこの中途半端な実装は。
ServiceControllerオブジェクトに対しps1xmlファイルでDescriptionやStartModeをScriptPropertyにして実装しとけよーと思った。せっかく拡張できるんだからさ。
- ----------------------------------------------------------------------------------------------------
- Select-String使えん・・・
- 2007年11月25日00:10
PS C:\script> select-string "aa" *.ps1
attrib.ps1:7: #
Get-Item?R?}???h???b?g??p???AAttributes?v???p?e?B??B
文字化けしとるがな
Shift-JISのファイルも検索・表示できないとはかなり終わってますね
せめて文字コードを指定できるようにしてくれー
UTF8はいけます
.NET Frameworkには文字コード判別のクラスとかないのかな・・・
前探してなかった気もする
文字コードを判別する: .NET Tips: C#, VB.NET, Visual Studio
http://dobon.net/vb/dotnet/string/detectcode.html
こういうごり押しが必要なのねー
あと&{スクリプトブロック} は、C#の{}空ブロックと同じことができるらしいー
要するに変数がその中でのみ使われてスコープ抜けたら破棄されるという
これを応用すればtrap文でtry catchみたいなこともできるらしいー
詳しくはPowerShellインアクションを買おう!w
2007/11/18
[質問]SecureStringオブジェクトって何に使うんでしょうか?
PowerShellのRead-Hostコマンドレットには-asSecureStringパラメーターってのがあって、これを指定するとSystem.Security.SecureStringオブジェクトが作れるのです(New-Objectでも作れるけど)。でもこれを何に使うかよくわかりません。何に使うんですか?
MSDNのサンプルは難しすぎて分からないし、「PowerShellインアクション」に書いてあることに意味があるとは思えないし。
なんか分かりやすい使い方ないですかー
ちなみにRead-Hostで入力するときに****で表示されるようになるのはいいんですけど、結果を普通のStringでも得られるようにしたほうがよっぽど使い道あると思うんですけど!SecureStringなんかPowerShellで使う場面あるのかー?
あらかじめSecureStringを作っておいて、Read-Hostで読み込んだ結果と比較してOKなら認証完了!みたいな使い方できんもんでしょうか。そもそもそういうものではなさそうな気もするんですが・・・
追記。あーExchange2007で使うのかー
http://winzenz.blogspot.com/2006/08/creating-new-mailbox-using-powershell.html
さらに追記。newpopsさんところに私の求めていたやり方が載っていましたー
元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/11/18/109024.aspx2007/09/27
Write-Errorコマンドレットの使い方
そもそもヘルプを読んでも使い方がよくわからないのですが、インアクションを読んで何となくわかりだしました。
Write-Error -exception Exception 2>&1|set-variable a
などのようにエラーパイプラインを標準パイプラインに結合させて、$aにSystem.Management.Automation.ErrorRecordオブジェクトが入ります。このオブジェクトにWrite-Errorで指定した各パラメータのエラー情報の各プロパティが格納されているわけですな。
こうして作ったErrorRecordオブジェクトは再利用可能で、
Write-Error -errorRecord $a
のように使える。同じエラーを複数回発生させたいときに使えるんじゃないでしょうか。
元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/09/27/98186.aspx2007/09/11
PowerShell イン アクション・Windows PowerShell実践スクリプティング―オブジェクト指向と集合指向の統合シェル刊行
Amazon.co.jp: Windows PowerShell イン アクション [イン アクションシリーズ]: 本: Bruce
Payette,株式会社クイープ
http://www.amazon.co.jp/Windows-PowerShell-%E3%82%A4%E3%83%B3-%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3-%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA/dp/4797337362
Amazon.co.jp: Windows PowerShell実践スクリプティング―オブジェクト指向と集合指向の統合シェル: 本: 豊田 孝
http://www.amazon.co.jp/Windows-PowerShell%E5%AE%9F%E8%B7%B5%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E2%80%95%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%81%A8%E9%9B%86%E5%90%88%E6%8C%87%E5%90%91%E3%81%AE%E7%B5%B1%E5%90%88%E3%82%B7%E3%82%A7%E3%83%AB-%E8%B1%8A%E7%94%B0-%E5%AD%9D/dp/4798017272/ref=pd_sim_b_1/249-8968983-2214760
前者は翻訳本です。家にあるんですがまだ読み切れてない・・・スクリプト文法・仕様にマニアックに取り組んだ本です。日本語版買っとこうと思います。
後者はどんな本でしょうね。これも買ってみないと。言語としてオブジェクト指向とからめた解説が読めるんでしょうか。
どんどん本が出ますね。まだ年末に向けて数冊出るとか出ないとかの噂ですよ。
元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/09/11/95267.aspx2006/09/20
[Bookmarklet]HTMLソースの表示
Bookmarkletとは、ブラウザのURL欄に入力して表示中のページに何らかのアクションを起こすスクリプトのことで、「お気に入り」や「ブックマーク」に指定することで再利用可能になります。ありがちだと思いますが、HTMLソースを表示するBookmarkletを書いてみました。
javascript:document.write(document.body.parentElement.outerHTML.replace(/\/g,">").replace(/\n/g,"
"));
実はこれを書いたのにはわけがあって、W-ZERO3[es]上で動作するPocketIEにソースを見る機能がないため作成したのですが…。肝心のPocketIE上ではうまく動作しません(泣
outerHTMLなどが取れないみたいですね。innerHTMLも駄目。writeとかalertとか使えるのに惜しいです。URLEncodeもなんか変で、\が%5Cにならず/になっちゃいます。最初これがエラーの原因かと思いましたが違いました。
でもOperaでは使えますよ!(なんか一昔前のWindows版ブラウザ事情とは逆だな〜)
元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/09/20/39209.aspx
Copyright © 2005-2018 Daisuke Mutaguchi All rights reserved
mailto: mutaguchi at roy.hi-ho.ne.jp
プライバシーポリシー