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

2015/06/14

わんくま同盟大阪勉強会#63でのセッション資料を公開します。

デモで使用したサンプルスクリプトも併せてご利用ください。

わんくま同盟の勉強会は今回の大阪#63で10年目に入ったのですが、実は私も9年前の大阪#4で勉強会セッションデビューをしていたりします。

さて、今回はPowerShell DSCリソースを作成するというテーマで話しました。DSCでは管理対象ごとにロジック(リソース)と設定(Configuration)を分離でき、一貫性を持ったインフラ構成の自動化が可能になる素敵な機能です。が、いかんせん、ビルトインリソース(OS標準で含まれるリソース)の種類が少ないので、実際の業務で使うにはカスタムリソースを作成する必要が出てくると思います。

今回のデモでは、テキストファイルの中身を自動構成するという、ごく単純なサンプルを作成して実演してみましたが、考え方や実装方法の基本はこれでカバーできるのではないかと思います。

より詳しくは、ぎたぱそ氏の記事を読んでいただければ良いかと思います。本番で使えるPowerShell DSCリソース作成入門 - Build Insider

サンプルスクリプトの説明
  1. 事前準備

    今回のデモはWin10 Insider Preview(PowerShell 5.0)上で行いましたが、PowerShell 4.0環境でもおそらく動作すると思います。なお、今回のConfigurationはローカルコンピュータに対しPush適用(Start-DscConfigurationコマンドレットによる手動適用)することを想定しています。あらかじめ、DSCが実行可能な環境(スクリプト実行ポリシー、PSリモーティング、Local Configuration Managerの設定等)を整えておいてください。また、スクリプトはすべて管理者権限で実行してください。

  2. xDSCResourceDesignerのインストール

    DSCリソースのひな形を作成するためのxDSCResourceDesignerをインストールします。v5環境であればPowerShellGetを用い、Install-Module xDSCResourceDesigner で入ります。v4環境の場合はTechnetからDSC Resource KitをDLしてください。

  3. xDSCResourceDesignerの使用方法の確認

    xDSCResourceDesignerの使用方法を確認します。make_Foo_resource_template.ps1を実行すると、xDSCResourceDesignerを用いてDSCリソースFooのひな形が$env:ProgramFiles\WindowsPowerShell\Modules\TestResourceに作成されます。ひな形がどのように作成されるかを確認してください。また、Get-DscResource -Name Fooとして、DSCリソースがきちんと認識されているか、確認してください。

  4. TextFileLineリソースの作成

    今回のデモで作成したTextFileLineリソースは、make_TextFileLine_resource_template.ps1を実行してまずひな形を作成しました。作成されたひな形を用いて、TextFileLine.psm1に実際のロジックを記述し、DSCリソースを完成させました。zipに含まれるTestResourceフォルダの中身が、今回作成したDSCリソースモジュールになりますので、まずは内容を確認してみてください。特に、Set-TargetResource関数はどのように実装すると冪等性を保持して作成できるのかを念頭に置いてみてください。

  5. TextFileLineリソースの展開

    zipに含まれるTestResourceフォルダを$env:ProgramFiles\WindowsPowerShell\Modules\の下にコピーしてください。

    Get-DscResource -Name TextFileLineとしてDSCリソースが認識されていることを確認してください。

  6. TextFileLineリソースを用いたConfigurationの作成

    start_dsc_configuration.ps1に含まれる、TextFileLineTestが、今回適用してみるConfigurationになります。

    start_dsc_configuration.ps1を実行すると、ドキュメントフォルダにmofファイルを生成し、Start-DscConfigurationコマンドレットにより設定を反映させます。

    正しくConfigurationが適用されれば、ドキュメントフォルダにlist.txtというファイルが生成し、中にプログラム言語のリストが記入されているはずです。

  7. Configurationが反映されたことを確認

    Test-DscConfigurationコマンドレットを実行すると、現在の状態とConfigurationに書かれた状態が一致していればTrueと表示されます。今はConfigurationを適用した直後なのでTrueになるはずです。

    またGet-DscConfigurationコマンドレットを実行すると、現在の各プロパティの状態を表示してくれます。

    list.txtに含まれる行をテキストエディタで編集して上書きしたりすると、Test-DscConfigurationの結果はFalseになるはずです。

    list.txtを手動で変更した状態で、再度start_dsc_configuration.ps1を実行すると、再びConfiguration通りの状態に戻ると思います。その際、変更のなかったプロパティに関しては、処理がスキップされていることをログをみて確認してください。

  8. その他

    Configurationを色々書き換えて試してください。例えばEnsure="Absent"にすると対象項目が存在しない状態になります。

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/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

2010/02/13

PowerShellは.NET Framework 2.0を利用するWindowsのシステム管理用シェルである。シェルであるためコンソールで対話的にコマンドを実行することができるのはもちろん、スクリプトファイル(*.ps1)を記述しバッチ的に実行することも可能である。ここではPowerShellスクリプトで(コンソールでも使用は可能だが)用いることのできる基礎文法を紹介する。なお、PowerShellでは文法上、大文字小文字を区別しない。

※(★2.0)の注釈があるものはPowerShell 2.0で新たに追加された要素である。

1.基礎

表示

コンソールに文字列を表示。

"Hello world" 

コマンドレット(後述)を使用した場合。

Write-Host "Hello world" 

コマンドレット

PowerShellはコマンドレットと呼ばれる100種類以上のコマンドライン・ツール群を単独で、あるいはパイプライン(後述)で連結して使用するのが基本となる。コマンドレットは原則verb-nounという命名規則にしたがっている。パラメータをつける場合は「-パラメータ名」あるいは「-パラメータ名 パラメータ値」を指定する。

# コマンドレットの一覧表示
Get-Command 

# サービスの一覧を表示
Get-Service 

# アプリケーション イベントログの最新15個のエントリを表示
Get-EventLog -logName Application -newest 15

パイプライン

コマンドレットが値を返却する場合、.NET Frameworkのオブジェクトが含まれる配列であることが多い。このオブジェクト配列がパイプラインを渡って後続のコマンドレットに入力される。

# プロセスのリスト(System.Diagnostics.Processオブジェクトの配列)を取得し、
# Where-Objectコマンドレットでハンドル数(handlesプロパティ)の値が500より大きいものだけを取り出し
# Select-Objectコマンドレットで最初の5つのオブジェクトだけを切りだして表示
Get-Process | Where-Object {$_.handles -gt 500} | Select-Object -first 5

# C:\Windows 配下のフォルダ、ファイルの一覧(System.IO.DirectoryInfo,System.IO.FileInfoオブジェクトの配列)を取得し、
# ForEach-Objectコマンドレットで配列を列挙しすべてのオブジェクトのFullNameプロパティ(フルパス)の値を表示
Get-ChildItem C:\Windows | ForEach-Object {$_.FullName} 

# 通常の配列に関してもパイプラインを使用可能。
# 重複を取り除き、ソートをかける
@(3,5,10,1,2,1,1,1,2,6,4,4)|Sort-Object|Get-Unique 

コメント

# コメント 

マルチラインコメント(★2.0)

<#
複数行に渡る
コメントです
#> 

変数の宣言

PowerShellは変数の宣言をしなくても変数を使用可能。以下のようにするとどのような型でも代入可能な変数が作られる。

$a = 1
$a = $b = $c = 1 #複数変数に一度に同じ値を代入する場合
$items = Get-ChildItem # コマンドレットの戻り値を格納 

変数の型を指定することは可能。以下のようにするとint型のみ格納可能な変数が作られる。

[int]$a = 1 

あるいは、コマンドレットを用いて$aという変数を宣言することもできる。この場合変数の型は指定できない。

New-Variable -name a 

変数のスコープ

# どのスコープからも読み書き可能
$global:a = 1

# 現在のスコープからのみ読み書き可能
$private:a = 1 

# 現在のスクリプトからのみ読み書き可能
$script:a = 1 

文法チェック

以下を実行することで未定義の変数を参照するとエラーが出るようになる。

Set-PSDebug -strict 

スクリプトの実行

デフォルトの実行ポリシーではスクリプトの実行は不許可であるため、以下のようにポリシーを変更しておく。(RemoteSignedはローカルにあるスクリプトファイルは無条件で実行可、リモートにあるスクリプトファイルは署名付きのもののみ実行可)

Set-ExecutionPolicy RemoteSigned 

スクリプト/コマンドを実行するにはコマンドラインで次のようにする。

コマンドを実行する

powershell -command {Get-ChildItem C:\} 

ファイルを実行する

powershell  .\script.ps1 

ドットソース(スクリプトの内容をグローバルスコープに読み込む)

powershell  . .\script.ps1 

ファイルを実行する(★2.0)

powershell -file script.ps1 

PowerShellスクリプトから別のスクリプトを実行する場合(関数のインクルードにも用いられる)

.\script.ps1
. .\script.ps1 # ドットソース 

デバッガの起動

Set-PSDebug -trace 2 

ステップ実行

Set-PSDebug -step 

2.数値

数値の表現

PowerShellにおける数値は.NET Frameworkの数値を表す構造体のインスタンスである。数値には整数、浮動小数点があり、変数に代入した段階で適切な型が設定される。

# int型(System.Int32型)
$int = 1

# System.Double型
$double = 1.001

四則演算

# 足し算
$i = 1 + 1

# 引き算
$i = 1 - 1 

# 掛け算
$i = 1 * 1 

# 割り算
$i = 1 / 1 

余りと商の求め方

# 割り算の余り
$mod = 7 % 3 

# 上記の場合の商
$div = (7 - 7 % 3) / 3 

べき乗

# 2の8乗
$i = [math]::Pow(2,8) 

インクリメントとデクリメント

# インクリメント
$i++ 

# デクリメント
$i-- 

3.文字列

PowerShellにおける文字列は.NET Frameworkの System.Stringクラスのインスタンスである。

文字列の表現

文字列はシングルクォーテーションかダブルクォーテーションで囲む。ダブルクォーテーションの中では`t(タブ)や`r`n(改行)などの特殊文字が使用でき、変数が展開される。

$str1 = 'abc'
$str2 = "def"
$str3 = "a`tbc`r`n" 

#変数展開(結果は abc def)
$str4 = "$str1 def" 

文字列操作

各種文字列操作

# 結合
$join1 = "aaa" + "bbb"
$join2 = [string]::Join(",",@("aaa","bbb","ccc") )

# 結合(★2.0)
$join2 = @("aaa","bbb","ccc") -join "," 

# 分割
$record1 = "aaa,bbb,ccc".Split(",") 

# 分割(★2.0)
$record2 = "aaa,bbb,ccc" -split "," 

# 長さ
$length = "abcdef".Length 

# 切り出し
$substr = "abcd".SubString(0,2) # ab

正規表現検索

# hitした場合はTrue,しなかった場合はFalse
$result = "abcd" -match "cd"

# 最初に見つかった文字列。添え字の1,2…には()内のサブ式にhitした文字列が格納。
$matches[0] 

正規表現置換

$result = "abc" -replace "c","d" 

4.配列

PowerShellにおける配列は.NET Frameworkの System.Arrayクラスのインスタンスである。

配列の参照と代入

# 5個の要素を持つ配列宣言と代入
$arr1 = @(1,3,5,7,9)
 
# 以下のようにも記述できる
$arr1 = 1,3,5,7,9 

# 型指定する場合
[int[]]$arr1 = @(1,3,5,7,9) 

# 1〜10までの要素を持つ配列宣言と代入
$arr2 = @(1..10) 

# 1要素の配列宣言と代入
$arr3 = @(1) 
$arr3 = ,1 

# 空の配列宣言と代入
$arr4 = @() 

配列の要素の参照と代入

# 4番目の要素を参照 
$ret = $arr2[3] 

# 6〜9番目の要素を含んだ配列を参照
$ret = $arr2[5..8] 

# 1〜4番目と8番目の要素を含んだ配列を参照
$ret = $arr2[0..3+7] 

# 配列の末尾の要素を取り出す
$ret = $arr2[-1] 

# 5番目の要素に値を代入
$arr2[4] = 11 

# 3より小さな要素を含んだ配列を返す
$ret = $arr2 -lt 3 

配列の個数

$arr1_num = $arr1.Length 

配列の操作

$arr1 = @(1,3,5,7,9) 
$arr2 = @(1..10) 

# 配列の末尾に要素を加える(push)
$arr2 += 50 

# 配列を結合し新しい配列を作成
$arr5 = $arr1 + $arr2 

# 配列にある要素が含まれるかどうか(ここではTrue)
$arr2 -contains 2 

5.ハッシュ

PowerShellにおけるハッシュは.NET Frameworkの System.Collections.Hashtableクラスのインスタンスである。

ハッシュ変数の宣言と代入

# 3つの要素を持つハッシュの宣言と代入
$hash1 = @{a=1;b=2;c=3}
 
# 空のハッシュの宣言と代入
$hash2 = @{} 

ハッシュの要素の参照と代入

# 要素の参照
$hash1.a 
$hash1["a"] 

#要素の代入
$hash1.b = 5
$hash1["b"] = 5 

ハッシュの操作

# ハッシュに要素を追加
$hash1.d = 4 
$hash1.Add("e",5)
 
# ハッシュの要素の削除
$hash1.Remove("a") 

# ハッシュのキーの取得
$keys = $hash1.Keys 

# ハッシュの値の取得
$values = $hash1.Values 

# ハッシュの要素を列挙
foreach ($key in $hash1.Keys)
{
    $key + ":" + $hash1[$key]
} 

# キーの存在確認
$hash1.Contains("b") 

6.制御文

if文

if (条件) {

}

if 〜 else文

if (条件) {

}
else{

}

if 〜 elsif 文

if (条件) {

}
elseif (条件) { 

} 

while/do文

while (条件) {

}

do {

} while (条件)

for文

for ($i = 0; $i -lt 5; $i++) {

} 

foreach文

foreach ($item in $items) {

} 

switch文

case を書かないのが特徴的。またスクリプトブロックを条件文に記述できる。

switch ($i) {
    1 {"1";break}
    2 {"2";break}
    {$_ -lt 5} {"5より小さい";break}
    default {"default句";break}
}
# ここで$iに配列を指定すると配列要素すべてに対してswitch文が実行される。 

比較演算子

比較演算子の一覧。PowerShellではPerlの文字列比較演算子のような記述をおこなうが、Perlとは異なり文字列も数値も同じ書式である。

$num1 -eq $num2 # $num1は$num2と等しい
$num1 -ne $num2 # $num1は$num2は等しくない
$num1 -lt $num2 # $num1は$num2より小さい
$num1 -gt $num2 # $num1は$num2より大きい
$num1 -le $num2 # $num1は$num2以下
$num1 -ge $num2 # $num1は$num2以上 

論理演算子

# 論理否定
$ret = -not $true
$ret = !$true

# 論理積
$ret = $true -and $false 

# 論理和
$ret = $true -or $false 

# 排他的論理和
$ret = $true -xor $false 

ビット演算子

# ビット単位の否定
$ret = -bnot 0x14F4

# ビット単位の積
$ret = 0x14F4 -band 0xFF00 

# 上記結果を16進数で表示する場合
$ret = (0x14F4 -band 0xFF00).ToString("X") 

# ビット単位の和
$ret = 0x14F4 -bor 0xFF00 

# ビット単位の排他的論理和
$ret = 0x14F4 -bxor 0xFF00 

7.サブルーチン

PowerShellのサブルーチンには関数とフィルタがある。関数とフィルタは呼び出し行の前で宣言する必要がある。 filter構文もfunction構文と並んで独自関数を記述するものだが、filter構文はパイプラインに渡されたオブジェクトをフィルタするのに用いる。 functionとの違いは、パイプラインに渡した配列を一度に処理するか(function)個別に処理するか(filter)

# 関数宣言の基本
function Get-Test {
    return "test"
}
# 注:returnを付けなくても関数内で出力された値はすべて呼び出し元に返却される。返却したくない場合は出力値をを[void]にキャストするか|Out-Nullに渡す。

# 引数を指定する場合
function Get-Test {
    param($param1,$param2)
    return $param1 + $param2
}
 
# 引数を指定する場合の簡易的な記述法
function Get-Test($param1,$param2) {
    return $param1 + $param2
} 

# 引数の型を指定する場合
function Get-Test {
    param([string]$param1,[string]$param2)
    return $param1 + $param2
} 

# 関数の呼び出し方(,区切りではなくスペース区切りであることに注意)
Get-Test "引数1" "引数2"

# 引数の順序はパラメータ名(引数名)を指定すると自由に指定可能
Get-Test -param2 "引数2" -param1 "引数1" 

# フィルタ宣言の基本
filter Get-Odd {
    if($_ % 2 -eq 1){
        return $_ 
    }else{
        return
    }
} 

# フィルタの使用
@(1..10) | Get-Odd

8.テキストファイル入出力

コマンドレットで可能。エンコーディングは日本語環境のデフォルトではShift-JIS。コマンドレット出力のテキストファイルへの書き出しに関してはリダイレクトも可能。この場合エンコーディングはUnicode。

$str1 = "testテスト"
Set-Content test.txt $str1 # 書き込み
Add-Content test.txt "追記" # 追記
$str2 = Get-Content test.txt # 読み込み

Set-Content test.txt $str1 -encoding UTF8 # UTF-8で書き込み

# リダイレクト
Get-Process > test.txt # 書き込み
Get-Process >> test.txt # 追記
Get-Process | Out-File test.txt -encoding UTF8 # エンコーディングを指定する場合

9.例外

PowerShellで例外が発生すると、デフォルトではエラーメッセージを表示し次の行を実行する(シェル変数$ErrorActionPreferenceの設定により挙動の変更可能)。VBでいうとOn Error Resume Nextに近い。エラーが発生すると$Errorにエラー情報の配列が格納され、$?にFalseが格納される。エラーをトラップするには次の構文を使用する。VBでいうとOn Error Goto lineに近い。

# すべてのエラーをトラップ
trap {

}

# エラーの型名を指定してトラップ
trap [System.Management.Automation.CommandNotFoundException] {

} 

# エラーを発生させる
throw "エラー"
throw New-Object NullReferenceException 

構造化例外処理(★2.0)

# 基本
try{

}
catch{

}
finally{

} 

# エラーの型を指定してcatch
try{

}
catch [System.Net.WebException],[System.IO.IOException]{

}

10.知っておいたほうがよい文法

行継続文字

1行にすると長いコードを複数行に書くには行継続文字`を用いる。VBの_。

$items = Get-ChildItem a*,b*,c*,d*,e* `
-force -recurce 

ただし以下のような場合は`を使用しなくてもよい

$items =
    Get-ChildItem a*,b*,c*,d*,e* -force –recurse
    
Get-Process | 
    Where-Object {$_.handles -gt 500} |
    Select-Object -first 5 

ステートメント分割

ステートメントを分割するには改行コードもしくは;を使用する。VBの:。JavaScriptと同様、文末に;はつけてもつけなくてもよい。

$i = 1; $j = 5; $k = $i + $j 

ヒア文字列

複数行の文字列を記述する方法。

$str = @"
aaaaaa
bbbbb
cccc
ddd
ee
"@ 

.NET Frameworkクラスの利用

.NET Frameworkに含まれているクラスのプロパティやメソッドを使用できる。基本的に完全修飾名を指定しなければいけないが、"System."は省略可能。また、intなど型エイリアスがいくつか定義されている。

# スタティックメンバの使用
[System.Math]::Pow(2,8) 

# インスタンスの生成とメソッドの実行
$arrayList = New-Object System.Collections.ArrayList
$arrayList.Add("a") 

# コンストラクタがある場合。複数ある場合は配列として指定
$message = New-Object System.Net.Mail.Message from@example.com,to@example.com

# COMオブジェクトの生成
$wshShell = New-Object -com WScript.Shell 

# デフォルトで読み込まれていないアセンブリを読み込む
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("hello!") 

# クラスにどんなメンバがあるかの確認
# インスタンスメンバ
Get-ChildItem | Get-Member 

# スタティックメンバ
[math] | Get-Member -static 

キャスト

-asを使った場合はキャスト失敗時もエラーにならずNullが格納される。

$dt = [System.DateTime]"2010/02/13"
$dt = "2010/02/13" -as [System.DateTime] 

ユーザー定義オブジェクト

PowerShellにはクラスを定義する構文はないが、空のオブジェクト(PSObject)を生成し、任意のプロパティ(ノートプロパティ)を付加することができる。

$obj = New-Object PSObject
$property = New-Object System.Management.Automation.PSNoteProperty "Name","名前"
$obj.PSObject.Members.Add($property) 

シェル変数

あらかじめ定義されている変数。シェル変数には自動変数(変更不可能)とユーザー定義変数(変更すると挙動を変更することができる)がある。自動変数の例を挙げる。

$_ :現在パイプラインにわたっているオブジェクト
$args :関数やスクリプトに与えられたパラメータの配列
$pshome :PowerShellがインストールされているフォルダのフルパス
$MyInvocation :スクリプトの実行情報。$myInvocation.ScriptNameでスクリプトのフルパス取得(★2.0)。$myInvocation.MyCommand.Path(1.0の場合)
$true :true。
$false :false。
$null :null。

 

サブ式

$()内には複数行のコードが記述できる。

$arr = $(1;2;1+4)

式モードとコマンドモード

PowerShellの構文解析は式モードとコマンドモードがある。式モードは通常のモード。コマンドモードは引用符がなくても文字列を文字列として扱う。コマンドレットのパラメータなどはコマンドモードで扱われる。ただしコマンドモードになるところでも()もしくは$()もしくは@()をつけるとその中身は式モードとして解釈、実行される。

$i = 1 + 1 # 式モード
Write-Host aaa # コマンドモード(表示:aaa)
Write-Host aaa bbb # コマンドモード(表示:aaa bbb)
Write-Host 1+1 # コマンドモード(表示:1+1)
Write-Host (1+1) # 式モード(表示:2)
$itemCount = @(Get-ChildItem).Length # 式モード

実行演算子とスクリプトブロック

&演算子を用いるとスクリプトブロック{}の内容を実行できる。この場合、スクリプトブロック内のコードは別スコープになる。

$script = {$i = 1+6; Write-Host $i}
&$script
& 'C:\Program Files\Internet Explorer\iexplore.exe' # パスにスペースの含まれるファイルを実行したりするのにも使える

フォーマット演算子

-f演算子を使うと、.NET Frameworkのカスタム書式が使用可能。

"{0:#,##0}Bytes" -f 38731362 # 表示:38,731,362Bytes

バイト数の簡易表記

$i = 1KB # 1024が代入される
$i = 1MB # 1048576が代入される
$i = 1GB # 1073741824が代入される

そのほかの基礎文法最速マスターへのリンク

プログラミング基礎文法最速マスターまとめ - ネットサービス研究室
http://d.hatena.ne.jp/seikenn/20100203/programmingMaster

PowerShellの詳しい機能解説についてはこちらの記事を参照してください。
PowerShell的システム管理入門 ―― PowerShell 2.0で始める、これからのWindowsシステム管理術 ―― ─ @IT
進化したPowerShell 2.0 ─ @IT

文法や機能について詳しく学びたい方には書籍もあります
Windows PowerShellポケットリファレンス
PowerShellによるWindowsサーバ管理術

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/02/13/186034.aspx

2009/10/04

リモート(ローカルでもいいですが)のPSSessionに入る(ファンインする)ためには、まずNew-PSSessionでPSSessionを作成し、Enter-PSSessionでそのセッションに入ります。面倒なので関数化してみました。

function Enter-ServerSession()
{
	$encryptedString="中略"
	$credential = New-Object System.Management.Automation.PSCredential "ユーザー名@ドメイン名",(ConvertTo-SecureString $encryptedString)
	$session = New-PSSession サーバー名 -Credential $credential
	Enter-PSSession $session
}

ここで、パスワードを毎回入力するのは面倒なので、関数の中に暗号化したものを直書きしてしまいます(もちろん、見てはいけない人に見られないところに関数を保存してくださいね)。

$encryptedStringにはパスワードの暗号化文字列を入れます。この文字列の取得方法は、

PS C:\> ConvertFrom-SecureString (Get-Credential).Password

と入力すると認証ダイアログが出るので、そこにユーザー名とパスワードを入れると、パスワードが暗号化された文字列を得ることができます。

なお、もちろん、PSSessionを張るサーバーへの認証が必要ない場合は、-Credentialの指定は要らないです。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/10/04/181825.aspx

2007/07/28

窓の杜 - 【NEWS】Windows Vista用ガジェットパック「nDigiGadgets」v0.11が公開、計16本を収録
http://www.forest.impress.co.jp/article/2006/09/21/ndigigadgets011.html

というガジェットシリーズの中に、nDigiTVProgramという、ライブドアのテレビ番組表RSSを取得して表示するガジェットがあり愛用しています。このガジェットの中でMicrosoft.XMLDOMを同期処理(xml.async = false;)で呼び出しているのですが、たまによくloadメソッド実行時に固まります。やっかいなのはガジェットは一つがフリーズすると全体を巻き込むところですね。非同期化するとこの問題は解消します。で、やり方は、

ローカルの XML ドキュメントをロードする (hPod)
http://hwat.sakura.ne.jp/hpod/200612/22-220000/

に載っています。

asyncプロパティをtrueにし、onreadystatechangeイベントに匿名関数を割り当てます。

...
	var xml = new ActiveXObject("Microsoft.XMLDOM");
	//xml.async = false;
	xml.async = true;
	xml.onreadystatechange = function (){
		if( xml.readyState == 4 ){
			// ロード完了時に、何かする
			var rdf;
....この中にもともとloadの後にあった処理を移動させる...
			statustext.title = "次のデータ取得は"+ mindate.getHours() + "時" + mindate.getMinutes() + "分\n今すぐ取得するにはクリック"
		}
	}
	xml.load(uri);


}

var lastLoadText = "";
...

てな感じで。

すべて他の方のネタでごめんなさいですー。私もガジェット作成続きしなきゃ・・・

ちなみにこのガジェットの作者さん、MSMVPを受賞されたそうです。おめでとうございますー。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2007/07/28/87488.aspx

2006/12/23

http://blogs.wankuma.com/mutaguchi/archive/2006/07/07/31826.aspx
の続きです。クリップボード中に複数URLが複数行に記述されている場合、それらをまとめてダウンロードするように改良しました。

Const adTypeBinary = 1
Const adSaveCreateNotExist = 1
Const adSaveCreateOverWrite = 2
 
sDLFolder = "C:\download"
 
Set Ie = WScript.CreateObject("InternetExplorer.Application")
'Ie.Visible = True
Ie.Navigate "about:blank"
Do While Ie.Busy Or Ie.ReadyState<>4
    Wscript.Sleep 10
Loop
'クリップボードの文字列を取得。
sClipBoard = Ie.Document.parentWindow.clipboardData.getData("text")  
Ie.Quit
 
If InStr(sClipBoard,vbCrLf) Then
    aURLs = Split(sClipBoard,vbCrLf)
Else
    aURLs = Array(sClipBoard)
End If
 
For Each sURL In aURLs
    If Left(sURL,5)="http:" Then
        sDest = sDLFolder & "\" & Mid(sURL,InStrRev(sURL,"/")+1)
        Set oHTTP = WScript.CreateObject("Msxml2.XMLHTTP")
        oHTTP.Open "GET", sURL, False
        oHTTP.Send
        Set Stream = WScript.CreateObject("Adodb.Stream")
        Stream.Type = adTypeBinary
        Stream.Open
        Stream.Write oHTTP.responseBody
        Stream.Savetofile sDest, adSaveCreateOverWrite
    End If
Next
 
msgbox "done"

ところがこれをIE7環境で実行すると毎回こんなダイアログがでるんですね。
このWebページがクリップボードへアクセスするのを許可しますか?
インターネットゾーンのレベルのカスタマイズで、「スクリプトによる貼り付け処理の許可」を「ダイアログを表示する」から「有効にする」に変えればダイアログは出なくなりますがお勧めしません。それよりは"about:blank"の代わりに何かローカルのファイルを指定してやったほうがいいです。ローカルのファイルはマイコンピュータゾーンで動作するのでダイアログが出ません。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2006/12/23/53018.aspx


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

Twitter

Books