2016/07/11

大変遅くなってすみません。7/2(土)、札幌で開催されたCLR/H #clrh101で行ったセッション、「PowerShell の概要と 5.x 新機能のご紹介」の資料を公開します。

札幌は涼しくて、何もかも美味しくて良かったです。夏の関西は人間の生存に適した気候とはとても言えないので、しばらく札幌に滞在していたかったですね。

さて、登録ページでのタイトルと若干違いますが、間もなく(8/2に)Windows 10 Anniversary Updateの登場とともにPowerShell 5.1の正式版が利用できるようになるということで、今回、5.0に加えて5.1の新機能もご紹介することにしたためです。(スライドは5.1の新機能の部分以外は、基本的にこれまでの内容と同様です。ご容赦ください。)

なお、PowerShell 5.1は現在のところ、Windows 10 Insider PreviewかWindows Server 2016 TP5で試すことができます。

PowerShell 5.0の登場からWindows Server 2016正式リリースまでの期間がけっこう空いたことと、ラピッドリリースの方針もあって、5.1が短期間で登場することとなりました。なのでどれが5.0でどれが5.1の機能かというのは割と曖昧ですけど、5.1で一番大きく変化するところは、PowerShellのエディションがDesktop EditionとCore Editionに分かれるところだと思います。

PowerShell Core Editionは従来のPowerShellのサブセット版となり、Windows Server 2016のインストールオプションの一つで、フットプリントを軽量化し、Windowsコンテナ技術に最適化された、Nano Serverで動作させることを目的として作られました。

Core Editionでは一部のコマンドレットのみのサポートとなりますが、基本的にNano Serverの管理はリモート経由でPowerShellを直接的あるいは間接的に用いて行うことになるため、当然ながら必須のコンポーネントとなります。

なお、PowerShell Core Editionは先日、1.0リリースを迎えたばかりの、.NET Core上で動作します。.NET Coreとは.NET Frameworkのサブセットで、マルチプラットフォームで動作し、OSSとして開発されています。

.NET Framework上で動作する、従来のフルセット版PowerShellも、Desktop Editionとしてこれまで通り利用可能です。

PowerShell 5.0、5.1の新機能はMSDNでまとめられているのでそちらもご参照ください。

Windows 管理フレームワーク (WMF) 5.0 RTM のリリース ノート概要 | MSDN
WMF 5.1 Release Notes (Preview)

追伸。Microsoft MVP for Cloud and Datacenter Managementを7月付で再受賞いたしました。おかげで今回のイベントで「関西MVP3人が集結」という触れ込みが嘘にならなくて良かったです。そして同じ分野でCLR/Hのスタッフでもある素敵なおひげさんも受賞されました。おめでとうございます。

2015/12/10

この記事はPowerShell Advent Calendar 2015の10日目の記事です。

はじめに

前編では、Invoke-WebRequestコマンドレットやWebClientクラスを用いて、WebページからHTMLの文字列を取得するところまで説明しました。

後編の今回は、取得したHTML文字列をパースして、オブジェクトとして利用可能しやすい形に変換する話です。

IEエンジンによるHTMLパース(DOM)

前編でも触れましたが、Invoke-WebRequestコマンドレットは、レスポンス文字列を取得すると同時に、HTMLをパース(構文解析)し、結果をオブジェクトとして構造化してくれます。

実はこのHTMLパース、内部的にInternet Explorerのエンジンを呼び出すことで実現されています。(ちなみに後で説明しますが、-UseBasicParsingパラメータを付与すると、IEエンジンを使わずごく基本的なパースのみ行うようになります。)

Invoke-WebRequestコマンドレットの出力であるHtmlWebResponseObjectオブジェクトのParsedHtmlプロパティを経由することで、HTMLパースされたオブジェクトを、DOM(Document Object Model)に従ってアクセスすることができます。(-UseBasicParsing指定時は不可)

HTMLのtable要素を切り出し、table各行を1オブジェクト、各セルをプロパティとして、オブジェクト配列化する例を以下に示します。

$response = Invoke-WebRequest http://winscript.jp/powershell/301

# DOMを利用して1つ目のtable要素を取得
$table = $response.ParsedHtml.getElementsByTagName("table")| select -First 1

# tableの1行目をプロパティ名として取得
$properties = ($table.rows| select -first 1).Cells| foreach {$_.innerText}

# tableの残りの行に対して、各セルのinnerTextをプロパティ値としてオブジェクト化
$objs = foreach($row in ($table.rows| select -skip 1))
{
    $row.Cells| foreach -Begin {
        $index = 0
        $obj = [ordered]@{}
    } -Process {
        $obj += @{$properties[$index] = $_.innerText}
        $index++
    } -End {
        [pscustomobject]$obj
    }
}

$objs| Format-List

ところで前編で軽く触れましたが、IEエンジンによるパースは、Invoke-WebRequestコマンドレットを用いずとも、以下のようにして直接IEのCOMインターフェースを呼ぶことで利用可能です。

$client = New-Object System.Net.WebClient
$content = $client.DownloadString("http://winscript.jp/powershell/301")
$parsedHtml = New-Object -com "HTMLFILE"
$parsedHtml.IHTMLDocument2_write($content)
$parsedHtml.Close()
$table = $parsedHtml.getElementsByTagName("table")| select -First 1
# 以下同様…

というより実際に試すと直接IEエンジンを呼び出す方がずっと速いです。理由はよく分かりませんが…。

HTML要素コレクションの取得

Invoke-WebRequestコマンドレットを用いると、DOMとは別に、すべての要素(AllElementsプロパティ)、input要素(InputFieldsプロパティ)、img要素(Imagesプロパティ)、a要素(Linksプロパティ)、script要素(Scriptsプロパティ)を含むコレクションを、HtmlWebResponseObjectオブジェクトの対応するプロパティからそれぞれ取得することができます。

コレクションに含まれる各要素は、innerText(タグ内の文字列)、innerHTML(タグ内のHTML)、tagName(タグ名)等のプロパティが共通して利用可能です。また要素の属性(たとえばa要素ならリンク先を示すhref属性)に、プロパティとしてアクセス可能となります。

以下はBingでWeb検索した結果から、ページタイトルとURLを抜き出す例です。HtmlWebResponseObjectのLinksプロパティでa要素の配列を取ってきて、次に検索結果では無いっぽいURLを、hrefプロパティの値を見てwhereで除外し、最後にinnerTextプロパティとhrefプロパティをTitle、Urlとリネームしてから値を出力しています。泥臭い処理が混じってますが、この泥臭さがスクレイピングなのかもなぁと思います。

$searchWord = "PowerShell 配列"
$notSearchResults = "/","#","javascript:","http://go.microsoft.com/"
$response = Invoke-WebRequest "https://www.bing.com/search?q=$([Uri]::EscapeDataString($searchWord))"
$response.Links | 
    where {
        $href = $_.href
        !($notSearchResults|? {$href.StartsWith($_)})
    }|
    select @{L = "Title"; E = "innerText"}, @{L = "Url"; E = "href"}|
    Format-List

form要素についてもほぼ同様にFormsプロパティからコレクションを取得できますが、このコレクションにはFormObjectという特別なオブジェクトが含まれます。FormObjectのFieldsプロパティは、Key=パラメータ名、Value=パラメータ値が格納された連想配列となっています。この連想配列は書き替えが可能なので、前編で説明した、ログオンを要するWebサイト等で用いると便利かと思います。

以下に、HtmlWebResponseObjectオブジェクトのプロパティをまとめます。(×印は使用不可を表す)

プロパティ名 説明 -UseBasicParsing
指定時
AllElements 本文に含まれるすべての要素のコレクション ×
Forms フォーム(form要素)のコレクション ×
InputFields 入力フィールド(input要素)のコレクション  
Images 画像(img要素)のコレクション  
Links リンク(a要素)のコレクション  
Scripts スクリプト(script要素)のコレクション ×

このように、一部のプロパティについては-UseBasicParsing指定時でも利用可能です。サーバーOS等でIEエンジンが利用できない場合には-UseBasicParsingパラメータが必須となりますが、その場合でも最低限のパースはしてくれるわけです。

HTML要素のコレクションを利用する方法は、DOMを使う方法に比べると自由度は少ないですが、「ページから画像のリストを取得したい」等の処理は簡便に行うことができます。

その他のHTMLパース手法

最後に、Invoke-WebRequestコマンドレットとIEエンジン以外のHTMLパース手法について軽くご紹介します。

XMLとしてパース(XHTML限定)

XHTMLというのはごくかいつまんで言うと、HTMLをXMLで定義したものです。XHTMLはXMLなので、XMLとしてパースして用いることができます。

PowerShellは[xml](XmlDocument)型アクセラレータと型アダプタにより、XML要素への簡便なアクセス手段を提供しています。以下のように、[xml]型アクセラレータを用い、取得したXHTML文字列を[xml]型に変換すると、以降は型アダプタの機能により、ドット演算子で要素を辿っていくことができます。

$client = New-Object System.Net.WebClient
$content = $client.DownloadString("XHTMLなページ")
$xml = [xml]$content
$xml.html.body.h2.'#text'

ただ世の中のWebページ上のXHTML文書が、すべてXML文書としてvalidなものであるかと言われると、現実はかなり厳しいです。そしてXML文書としてエラーがある場合は、型アクセラレータの処理は容赦なく失敗します。なのでこの手法は「使えたら強いが、大抵使えない」レベルのものと思って頂ければいいと思います。

SgmlReader

標準機能にこだわらなければ、.NET製のHTMLパーサーを使うのが楽かと思います。SgmlReaderは通常のHTML文書(当然、XHTMLに限らず)をXmlDocumentへとパースしてくれるので、PowerShellと相性が良いのではないかと思います。

以下にサンプルを載せておきます。

Add-Type -Path .\SgmlReaderDll.dll

function Get-HTMLDocument
{
    param([uri]$Uri)
    $sgmlReader = New-Object Sgml.SgmlReader -Property @{
        Href = $Uri.AbsoluteUri
        CaseFolding = [Sgml.CaseFolding]::ToLower
    }
    $doc = New-Object System.Xml.XmlDocument
    $doc.Load($sgmlReader)
    $doc
}

$xml = Get-HTMLDocument http://winscript.jp/
$xml.html.body.div|? id -eq outer|% div|? id -eq main|% {$_.p.innerText}

ぎたぱそ氏も以前SgmlReaderを取り上げておられるので、そちらも参考にして下さい。:Html Agility Pack と SgmlReader を使って PowerShell でスクレイピングしてみる - tech.guitarrapc.cóm

正規表現等で自前パース

これまではHTMLパースを既存のコマンドやライブラリを用いて行ってきましたが、対象のHTMLが非常にシンプルである場合とか、HTMLですらなく単なるテキストの場合だとか、対象ページは分量が多いものの必要箇所はごくわずかで、かつピンポイントに取得可能な場合等々は、むしろ自前でパースするコードを書いた方が手っ取り早いこともあります。

例えばYAMAHAのルーターで、管理Webのシステム情報レポートからグローバルIPアドレスを取ってくる、みたいなことは、

$response = Invoke-WebRequest http://サーバー/detail/status.html -UseBasicParsing -Credential $credential
if($response.Content -match "PP IP Address Local\: (.+)\,")
{
    $ipAddress = $Matches[1]
}

のようなコードで十分かと思います。

ConvertFrom-String

これはまだ検証してないんですが、PowerShell5.0の新機能、Auto-Generated Example-Driven Parsingの実装であるConvertFrom-Stringコマンドレットを用いて、HTMLパースができないかな、と考えています。

ConvertFrom-Stringについては過去記事参照:[v5] Auto-Generated Example-Driven Parsing について - PowerShell Scripting Weblog

まとめ

前後編に渡って、PowerShellでのWebスクレイピングの手法について解説しました。スクレイピングはWeb APIが用意されていない場合の苦肉の策ですが、背に腹は代えられない場合というのは稀によくあると思います。そういうときに今回の記事が参考になれば幸いです。

次回あたりには、Web APIがちゃんと用意されてる場合に、PowerShellから利用する話をやろうかと思います。

2015/12/04

この記事はPowerShell Advent Calendar 2015の4日目の記事です。

はじめに

今回はPowerShellでWebページのスクレイピングをする際の、ちょっとしたノウハウ集を前後編に分けて紹介したいと思います。

スクレイピングというのは、Webページから文字列を取ってきて、スクリプトから利用可能な形に加工する処理です。昨今は多くのWebサイトやサービスでWeb APIが公開されていて、スクレイピングをせずとも比較的簡単にデータを取得できます。PowerShellだとInvoke-RestMethodコマンドレット等が使えます(その話はまた次回とかにやります)。

しかし現実には、APIが公開されていない等の理由で、HTMLを取ってきて自前で解釈せざるを得ないケースが多々あります。さて、PowerShellではどうやりましょうか、というのが今回の話。様々な方々によってもう色々と語られている分野ではあるのですが、結構細かいハマりどころがあるのでちょっとまとめてみようと思いました。

前編ではまず、Webページからの文字列の取得方法ついてまとめます。

なお、スクレイピングには技術的な問題以外の、微妙な問題(著作権の問題とか、Webサイトへの攻撃と見なされる可能性とか)を含むものなので、その辺りは各自どうかご留意ください。この辺りの話はPowerShellに限った問題ではないので、ここでは詳説いたしません。参考記事:Webスクレイピングの注意事項一覧 - Qiita

Invoke-WebRequestコマンドレットで文字列を取得する

PowerShellでのスクレイピング、基本は何はなくともInvoke-WebRequestコマンドレットです。ただしこのコマンドレットはPowerShell 3.0で追加されたものなので、2.0環境にはないことに注意です。その場合は.NETのWebClientクラス等を使う方法があり、後で述べます。

基本は、

$response = Invoke-WebRequest -Uri "http://winscript.jp/"

のように、Invoke-WebRequestコマンドレットを実行する、だけです。「-Uri」は省略可能です。

このとき$responseにはHtmlWebResponseObjectオブジェクトが格納されています。このうち、指定URLのWebページに含まれているHTMLなどの文字列データは、Contentプロパティに格納されます。つまり、$response.Content に欲しいデータが格納されているので、あとはそれをよしなに利用すればいいわけです。

実はInvoke-WebRequestは、文字列データを取得すると同時に、HTMLの場合はパースしてタグの構造をオブジェクト化までしてくれます。が、それについては次回。

なお、Invoke-WebRequestコマンドレットでは文字列を取得する他、バイナリデータをダウンロードしてファイルとして保存する機能もあります。それについては過去記事をご参照ください。

リクエストにパラメータを付与する(GET)

GETメソッドを用いてクエリを指定する場合、要はhttps://www.google.co.jp/search?q=PowerShell のようなURLのデータを取得する場合は、Invoke-WebRequest "https://www.google.co.jp/search?q=PowerShell" のようにQueryStringを含んだURLをそのまま指定するだけでOKです。

ただし、動的にクエリを組み立てる場合は、URIエンコード(URIエスケープ)を考慮する必要があります。もっとも簡単なのは

$searchWord = "PowerShell 配列"
$response = Invoke-WebRequest "https://www.google.co.jp/search?q=$([Uri]::EscapeDataString($searchWord))"

のように、Uri.EscapeDataStringメソッドを使う方法かと思います。

リクエストにパラメータを付与する(POST)

POSTメソッドでリクエストボディにパラメータを付与するには、Invoke-WebRequestコマンドレットの-Methodパラメータに"Post"を指定し、-Bodyパラメータにリクエストボディに付与するデータを連想配列で指定します。

たとえばブログのトラックバックを手動で撃つにはこんな感じでいけます。

$body = @{title="テスト";url="http://example.com/";excerpt="テスト";blog_name="test"}
Invoke-WebRequest http://ご自分のブログのトラックバックpingURL -Method POST -Body $body

なお、リクエストボディに含めるパラメータの各値(連想配列の値)は、自動でURIエンコードしてくれます。

(12/16追記)
また、-Bodyには連想配列のみならず、任意の文字列(URIエンコード要)やバイト配列(バイナリを送信する場合)を指定することも可能です。

標準認証が必要なページを取得する

ページの取得に標準認証が必要な場合は、-Credentialパラメータにユーザー名とパスワードを指定したPSCredentialオブジェクトを指定すればOKです。

セキュリティのことは取りあえず置いておき、簡易的にスクリプトに生パスワードを直書きしてもいいかな、という場合には以下のように書くことができます。

$userName = "user"
$password = "pass"
$credential = New-Object PSCredential $userName, (ConvertTo-SecureString $password -AsPlainText -Force)
Invoke-WebRequest 認証が必要なページのURL -Credential $credential

しかしこの方法はもちろんお勧めできないので、スクリプトとして保存する場合は通常はパスワードを暗号化しておきます。

まず、Get-Credential ユーザー名 | Export-Clixml cred.xmlを、スクリプトを実行するコンピュータ上で、スクリプトを実行するアカウントと同じアカウントで実行します。パスワードを入力するダイアログが出るので、Webサイトにログオンする際のパスワードを入力します。すると、ユーザー名と暗号化されたパスワードがcred.xmlに出力されます。

スクリプトからは

$credential = Import-Clixml cred.xml
Invoke-WebRequest 認証が必要なページのURL -Credential $credential

のようにすると、cred.xmlからユーザー名と復号したパスワードを、そのまま-Credentialパラメータに渡すことが可能です。

なおcred.xmlに含まれる暗号化パスワードは、ConvertFrom-SecureStringコマンドレットと同様、Windows Data Protection API(DPAPI)を用いてWindowsアカウントのパスワードをキーに利用して暗号化されているので、他のユーザーが復号することはできません。

ちなみに同一スクリプトファイルに暗号化パスワードを含めておくこともできなくはないです。過去記事参照。あと本当は資格情報マネージャーを使うのがいいんですが、…略。参考:PowerShell で Windows の 資格情報マネージャー を利用する (Jenkins などでの Git Credentialなど) - tech.guitarrapc.com

セッション情報を引き継ぐ

多くのWebアプリケーションは、同一クライアントからの連続したアクセスを、セッションという単位で管理します。

サーバーはクライアント(普通はWebブラウザ)の初回アクセス時にセッションIDを含むcookieを返し、クライアントからの2回目のアクセス時に、サーバーはcookieにセッションIDが含まれているかどうかを確認し、同一クライアントからのアクセスかどうかを判断するわけです。(ざっくりした説明ですが)

WebブラウザではなくInvoke-WebRequestを使ったアクセスでも同様に、以下のようにすれば受けとったcookie等のセッション情報を次回アクセスに引き継ぐことができます。

$url = "https://ログオンが必要なサイト"
$body = @{リクエストボディ(例えばユーザー名とかパスワードとか)}
$response = Invoke-WebRequest $url -SessionVariable sv -Method POST -Body $body
Invoke-WebRequest $url -WebSession $sv

初回アクセス時に-SessionVariableパラメータに指定した変数名(sv)の変数($sv)にはWebRequestSessionオブジェクトが格納されます。この中に、サーバーから受け取ったcookie等の情報が格納されています。

次回アクセス時には、-WebSessionパラメータに、初回アクセス時に得られたWebRequestSessionオブジェクト($sv)を指定します。

さて、実際のWebアプリケーションではcookie以外にも、Formのhiddenフィールドの値などもセッション管理に用いていることがあります。その場合は、初回アクセスのレスポンスからFormに含まれるinput type="hidden"なフィールドを抽出し、次回アクセスのリクエストボディに含ませる必要が出てきます。この辺りの話は後編で述べるパースが必須になってくる(し、長くなる)ので今回は詳説しません。Invoke-WebRequestコマンドレットのリファレンスのExample2に、Facebookにログオンする例なんてのがあるので、そちらで雰囲気をつかんでください。(今でも動作するかは確認してないですが)

エラートラップ

さて、Invoke-WebRequestは、タイムアウトになった、名前解決ができなかった、ページが無かった(404エラー)等々、正常にWebページを取得できなかった場合は、System.Net.WebExceptionというエラーを出します。

コマンドレットの出すエラー(Errorストリームに出力されるErrorRecord)は、try...catchステートメントでは捕捉できない、というのが原則ですが、Invoke-WebRequestコマンドレットのエラーは一般的なコマンドレットと異なり、普通の.NETの例外(System.Net.WebException)なので、try...catchステートメントでエラートラップを行います。

とは言え、Invoke-WebRequestコマンドレットの仕様上、エラートラップをして適切な処理を行うのは非常にめんどいです。何故かというと、Invoke-WebRequestがエラーを出した時点で、HtmlWebResponseObjectオブジェクトの出力は行われないので、このオブジェクトから得られる様々な情報(レスポンス文字列、ステータスコード等々)が取得できないからです。

じゃあどうすればいいのかという話なんですけど、どうもWebExceptionオブジェクトのResponseプロパティを見るしかないようです。具体的にはこんな感じ。

try
{
    $response = Invoke-WebRequest http://存在しないページなど
}
catch [System.Net.WebException]
{
    # HTTPステータスコード取得
    $statusCode = $_.Exception.Response.StatusCode.value__

    # レスポンス文字列取得
    $stream = $_.Exception.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader $stream
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd()
}

せっかくInvoke-WebRequestコマンドレットは、生のレスポンスを利用しやすくHtmlWebResponseObjectという形で返してくれるのに、エラー発生時はその恩恵を受けることができず、泥臭い処理が必要になります。これはかなりいけてないですし、どうせここまで書かないといけないのであれば最初からWebClientクラスを使った方がいいと思います。

httpsで無効な証明書が使われている場合

(12/16追記)
Invoke-WebRequestコマンドレット(およびWebClient)では、httpsで始まるURLからもダウンロード可能ですが、サイトで用いられている証明書に問題がある場合(期限が切れている、暗号化形式に問題がある、いわゆるオレオレ証明書である等)には、「要求は中止されました。SSL/TLSセキュリティで保護されているチャネルを作成できませんでした」というエラーが出てしまいます。

これを回避するには、Invoke-WebRequestコマンドレット実行前に、

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

という1文を記述しておきます。

ただし、証明書に問題があるということは、その通信相手が正当かどうか、通信内容が正しく秘匿されているかどうか、保証がされなくなるということですから、その点は念頭においてください。

文字化けの問題

Invoke-WebRequestコマンドレットのもう一つの悩ましい問題、それは文字コードです。実はInvoke-WebRequestコマンドレットには、Webページの文字コードを指定する方法がありません。(多分)

ではレスポンス文字列の文字コードがどのように決まるかというと、サーバーが返すレスポンスヘッダのContent-Typeフィールドで指定されているcharsetです。具体的には、$response.Headers["Content-Type"]の値が例えば"text/html; charset=UTF-8"であれば、$response.Contentの文字コードはUTF-8になります。

このときページ(HTML)を記述している文字コードと、レスポンスヘッダで指定されている文字コードが一致すれば全く問題はないのですが、異なる場合は容赦なく文字化けします。

異なる場合だけでなく、レスポンスヘッダのContent-Typeフィールドに文字コードの指定がない場合はASCIIと見なされるので、日本語のページの場合はやはり文字化けします。

この問題を回避する方法は、私はまだ見つけていません。よって文字化けが起きる場合は、諦めてWebClientを使って文字コードを指定するようにしています…。

WebClientを用いる

以上で述べてきたとおり、Invoke-WebRequestコマンドレットは、ページをさくっと取得して、さくっとパースするのには重宝するのですが、細かい所で融通が利かない印象があります。

そこで細かい処理が必要な場合(と、PowerShell 2.0環境)は、素直にWebClientクラスを用いるのがいいと思います。今回WebClientの使い方も入れようかと思いましたが、長くなったので詳しくは省略します。

基本は以下のような感じでDownloadStringメソッドを使って文字列を取得します。文字コードも指定できます。

$client = New-Object System.Net.WebClient
$client.Encoding = [System.Text.Encoding]::UTF8
$content = $client.DownloadString("http://アドレス")

なお、WebClientを用いた場合でも、Invoke-WebRequestと同等のHTMLパースを行う方法は存在するので、それは次回に。

おわりに

今回はまず、Webページから文字列データを取得する部分にフォーカスしてみました。といっても、Invoke-WebRequestの機能を全部網羅したわけではなく、使用頻度が高そうなものと個人的ハマリポイントがあるところだけです。なので詳しくはリファレンスを見て下さい。というかハマリポイントたぶんまだまだ一杯あると思います。

後編では、とってきた文字列データを「パース」して、扱いやすいデータ形式に変換する方法についてまとめようかと思います。

2013/03/24

Windows 8[業務アプリ]開発読本:書籍案内|技術評論社

9784774156057

というわけで、私が企画協力させていただいた「Windows 8 [業務アプリ] 開発読本」という書籍が技術評論社さんから3/27に発売となります。この書籍は書名通り、Windows 8対応の業務アプリを作成するためのノウハウを、私のお友達を中心とした皆さんによって書かれたものとなります。執筆者を募集したときは特に意識はしてなかったのですが、結果的にMicrosoft MVPやMicrosoftエバンジェリストをはじめとしたエキスパートで占められるという何とも豪華な執筆陣となっています。

Windows 8の開発書籍ということで、Windowsストアアプリ開発法をメインに据えた構成となっているのはもちろんのこと、従来のデスクトップ(Windows Forms / WPF)アプリとの違いや移行方法、ストアアプリとデスクトップアプリをどう作り分けていくか(既存のデスクトップアプリのうちどこをストアアプリ化するのか)、といった視点も盛り込んでいます。

というのもストアアプリはタッチデバイスを想定した新しいプラットフォームではあるものの、業務システムすべてをストアアプリに置き換えるというのは得策とは言えないケースが多々あるためです。逆に業務システムのうちストアアプリ化すると効率が上がる部分も多く存在しており、本書では業務システムをグレードアップさせるためのストアアプリ開発を、概念、UI設計から具体的なコードにいたるまで詳しく取り上げています。

私もWindows 8業務アプリを開発する上で、DevOps(開発と運用の協調)を実現するためにPowerShellモジュールをアプリに組み込み、運用サイドでもアプリ操作の自動化を容易に行う方法についての記事を書かせていただいてます。

本書はストアアプリを中心にクラウド等の周辺テクノロジーを包括した、Windows 8時代の業務システム開発とはどのようなものであるか、どのように行っていけばいいのか、という指針を提示できているのではないかと思っています。Windows開発に携わる方はぜひお手にとって見ていただきたい一冊です。どうぞよろしくお願いします。

3/29追記。
本書のサポートページが公開になりました。特集1と私の記事のサンプルファイルがダウンロード可能になっていますので、ご活用ください。

2013/02/13

【改訂新版】Windows PowerShellポケットリファレンス:書籍案内|技術評論社 [表紙]【改訂新版】Windows PowerShellポケットリファレンス

というわけで、2/23に拙著「【改訂新版】Windows PowerShell ポケットリファレンス」が発売となります。初版は2008年に出版され、ご愛顧いただいていたのですが、このたびPowerShell 3.0 / Windows 8 / Windows Server 2012に対応して改訂新版として装いも新たに再登場となります。

今回はPowerShellの基礎知識や文法、リモート接続方法等を項目ごとに解説した「Part1 PowerShellの基礎」、PowerShell 3.0に含まれる全コアコマンドレットを分野別に分類し、その構文、パラメータと使用例を含め解説した「Part2 コマンドレット」、PowerShellから利用する頻度の高い.NET Frameworkクラスに含まれるメソッドとプロパティを解説した「Part3 .NETクラス」、Windows 8 / Server 2012に含まれる全モジュールとそれに含まれる全コマンドレット(2000以上!)のリスト(コマンドレットごとにも一言説明文付き)を掲載した「Part4 モジュール」の4部構成となっております。

PowerShell 1.0/2.0/3.0全対応で、2.0以上で対応の機能、3.0で対応の機能にはそれぞれ分かりやすくマークを入れてあります。

今回もいくつかサンプルスクリプトを掲載しており、それはサポートページでダウンロードできるようにする予定です。

392ページだった初版から大幅増ページで592ページとなり、ポケットリファレンスといいつつ本当にポケットに入るのか?というボリュームとなっていますが、きっと皆様のPowerShellライフのお役に立てるものと思います。書店等で見かけた際は是非、お手に取ってご覧になっていただきたいと思います。

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で発表してください!!

2012/02/27

昨年末に実施したPowerShell Advent Calendar 2011の記事がGIHYO DIGITAL PUBLISHING(技術評論社の電子出版サイト)さんにより電子書籍化されました。

ダウンロードは無料ですが、ダウンロードには会員登録(無料)が必要になります。HTML形式およびEPUB形式での提供となります。EPUB形式の場合はEPUBリーダーが必要となります。WindowsだとEPUBReader(Firefoxのアドオン)などで読めます。

ぜひダウンロードして読んでみてください! 私はタブレット端末を持っていないのですが、これを読むために欲しくなりました。こういう形で電子書籍が身近になるのはよいですよね。2011年に実施された技術系アドベントカレンダーは多数ありますが、その中のいくつかが順次電子書籍化されていっていますので、ほかのカレンダーについても要注目ですね。

2011/11/14

PowerShell Advent Calendar 2011というイベントを12/1より開催します。
こちら:PowerShell Advent Calendar 2011 : ATND

現在、参加者募集中です。

以下はイベント告知ページのコピペです。

概要

今年はPowerShellもAdvent Calendarをやりましょう!
Advent Calendarとは技術系コミュニティで行われるイベントの一つで、ある特定技術分野をテーマに、12/1から12/25までの25日間、参加者が交代で毎日ブログ記事を書いてクリスマスを迎えるイベントです。

ルール

PowerShell Advent Calendar 2011ではその名の通り、Windows PowerShellに関する記事を参加者みんなで書いていきます。Tips、コード、等々、PowerShellに関する内容であれば何でもOKです。基本的にATNDの参加表明順が執筆担当日になります。たとえば5番目に参加表明した方は12/5の記事を執筆します。参加者の方は担当日に記事をご自分のブログに書いていただき、そのURLをコメント欄に書いてください。都合で担当日に投稿できない、担当日を変えてほしいなどがありましたらコメント欄でお願いします。

参加者が25人に達した時点で締切りです。逆に期日までに参加者が現れない場合は、すでに執筆した参加者が再度執筆しても良いこととします(その場合はコメント欄で宣言を)。

あなたの参加、お待ちしています!
ぜひがんばって完走させましょう!

参考記事

ここまでコピペでした。

まだ参加希望者が少ないのですが、ぜひぜひ、ご参加くださいませ。お題はPowerShellがからんでいれば何でもOKです。PowerShellを触ってみた感想とか、試しにこういうコマンドを実行してみたorコードを試しに書いてみた、など何でも結構です。Advent Calendarはゆるふわなイベントなのであまり身構えず気軽にご参加いただけると嬉しいです。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2011/11/14/212019.aspx

TechNet マガジン October 2011内のWindows PowerShell: 空白文字を入れてくださいという記事に対する意見です。

PowerShellスクリプトを記述するときは適宜空白文字やインデントを入れて見やすくしましょう、という趣旨の記事なんですが、その趣旨には同意するものの、どうも実際にインデントを入れたスクリプトの例がいけてない気がしました。

ここでインデントや改行などを適切に追加して体裁を整えたとされるコードは次のようなものです。

(ブログのスタイルに起因する見え方の違いが考えられるので、比較のためにオリジナルも再掲させていただきます)

function Get-PCInfo {
[CmdletBinding()]
param(
        [Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True)]
        [string[]]$computername
    )
    PROCESS {
        Write-Verbose "Beginning PROCESS block"
foreach ($computer in $computername) {
            Write-Verbose "Connecting to $computer"
            try {
                $continue = $true
                $cs = Get-WmiObject -EV mybad -EA Stop `
-Class Win32_computersystem `
-ComputerName $computer
            } catch {
                $continue = $false
                $computer | 
Out-File -FilePath oops.txt -append
Write-Verbose "$computer failed"
                $mybad | ForEach-Object { Write-Verbose $_ }
            }
            if ($continue) {
                $proc = Get-WmiObject win32_processor `
-ComputerName $computer | 
                    select -first 1
                $obj = new-object -TypeName PSObject
             $obj | add-member NoteProperty ComputerName $computer
             $obj | add-member NoteProperty ProcArchitecture $proc.addresswidth
             $obj | add-member NoteProperty Domain $cs.domain
             $obj | add-member NoteProperty PCModel $cs.model
                $obj.psobject.typenames.insert(0,'MyPCInfo')
                write-output $obj
            }
        }
    }
}

最初見た時、全体的に何がなんだか分からなかったのですが、よくよく見てみると一応ルールはあるようで、一つの文を改行して複数行に記述する場合は、二行目以降は文頭から詰めて記述する、というルールに従っているようです。

こういう書き方はかえって読みづらいと感じてしまうのは私だけでしょうか? 確かに、Webページや書籍などで一行がスペースに収まらないときに強制的に改行して表示する場合にこのような体裁になることはありますが、これははっきり言って読みづらいです。

せっかく自分で改行を入れるわけですから、改行したときも読みやすくしたほうがいいでしょう(そもそも強制改行されて読みづらくなるというのを防ぐというのも、自分で改行を入れる意義の一つなわけですから)。

あとparam()やforeach{}のインデント位置はそもそもなぜそうなのかよくわかりませんね(単なる編集ミスだろうか?)。

この辺を踏まえて、私流にインデントを入れるとこんな感じです。

function Get-PCInfo
{
    [CmdletBinding()]
    param
    (
        [Parameter(
            Mandatory=$True,
            ValueFromPipeline=$True,
            ValueFromPipelineByPropertyName=$True
        )]
        [string[]]$computername
    )
    
    process
    {
        Write-Verbose "Beginning PROCESS block"
        foreach ($computer in $computername)
        {
            Write-Verbose "Connecting to $computer"
            try 
            {
                $continue = $true
                $cs = Get-WmiObject -EV mybad -EA Stop `
                      -Class Win32_computersystem -ComputerName $computer
            }
            catch
            {
                $continue = $false
                $computer | 
                    Out-File -FilePath oops.txt -append
                Write-Verbose "$computer failed"
                $mybad | ForEach-Object { Write-Verbose $_ }
            }
            if ($continue)
            {
                $proc = Get-WmiObject win32_processor `
                        -ComputerName $computer |
                            select -first 1
                $obj = new-object -TypeName PSObject
                $obj | add-member NoteProperty ComputerName $computer
                $obj | add-member NoteProperty ProcArchitecture $proc.addresswidth
                $obj | add-member NoteProperty Domain $cs.domain
                $obj | add-member NoteProperty PCModel $cs.model
                $obj.psobject.typenames.insert(0,'MyPCInfo')
                write-output $obj
            }
        }
    }
}

こんな感じでPowerShellも基本的には他の言語のコード整形の流儀に準じればいいと思います。唯一PowerShellで特徴的なのは複数行に渡るパイプラインの記述方法になると思いますが、そこも特に深く考えずに一段インデントを深くする程度でいいのではないかと思います。

あと「一貫性」の節で、「{」を改行の前に入れるか後に入れるかは統一させよとありますが、基本的にはそれでいいと思います。ただ改行の後に「{」を入れるスタイル(私の書いた例)でも、改行後に「{」や「(」を入れるとコードの意味が変わったりエラーになったりする場合が出てくるので、その場合はわざわざ継続文字「`」を入れてやらずとも、その部分だけは改行前に入れてやってもいいかなと思います。

具体的にはこのコードでいうと「[Parameter( 」のところなどですね。あとスクリプトブロックをパラメータに取るコマンドレットの場合なんかもそうです。

@(1..12)|ForEach-Object {
    Write-Host $_
    Write-Host ($_ * $_)
}

こういうのですね。この場合ForEach-Objectコマンドレットの-processパラメータ(ここではパラメータ名は省略されている)にスクリプトブロックを指定しているのですが、「{」はスクリプトブロックリテラルの開始文字なので、改行後に入れると正しく動作しません。

PowerShell ISEはまだVisual Studioなどに比べると編集機能が貧弱で、構文を元に自動的に整形してくれたりしないので、ユーザーが自分ルールで整形していかないといけないです。しかし基本的には他の言語と同様な、素直な整形を施してやれば特に読みづらくなるということもないかなと思います。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2011/11/14/212009.aspx

2011/10/09

WindowsサイドバーガジェットはWindows Vistaの登場で追加されたプログラムで、デスクトップ上にガジェットと呼ばれるミニプログラムを貼り付けることができます。ガジェットはHTML/CSS/J(ava)Script (+VBScript)で記述することができます。Windows7の登場で名称が「Windowsデスクトップガジェット」と変更され、一部仕様に変更が加えられたものの現役でした。

ところが先月末(2011/09)頃に、サードパーティー製のものを含む多数の追加ガジェットのWindows Live Galleryでの公開が中止されました。現在は登録されたガジェットのリストは表示されるものの、ダウンロードができない状態です。まもなくサイト自体が閉鎖されるものと思われます。

現時点でWindows7で「ガジェット」を実行し、そこに表示される「オンラインで追加のガジェットを取得」リンクをクリックするとデスクトップ ガジェット - Microsoft Windowsというページに飛ばされますが、ここでは(おそらく人気上位であった)ガジェットが数点のみダウンロードできるという状態です。

この措置に対するMicrosoftの公式コメントがこちらになります。Looking for gadgets? - Downloads - Microsoft Windows

この記事を要約すると

  • MicrosoftはWindows Live Galleryを閉鎖し、今後、新しいガジェットの開発およびアップロードをサポートしない
  • ただし人気ガジェットはまだダウンロードできるようにしておくよ
  • ガジェット製作者はよりリッチなプラットフォームであるMetro Style Appにシフトしてね
  • でもまだガジェットに興味がある人もいるだろうから一応開発ドキュメントは残しておくよ
  • まだガジェットを公開したいのならCodePlexでどうぞ

という感じになるかと思います。まだPreview版しか出てない次期Windowsでしか動かないMetro Style Appを移行先に指定するのはかなり無理があるように思いますが、どうもMicrosoftはMetroと技術的にかぶるガジェットをさっさと亡きものにしたいようです。Windows 8のDeveloper Previewのクラシックデスクトップでは一応、今のところはデスクトップガジェットの機能は削除されていませんが、これからの開発の過程で削除されてしまう可能性も十分にありそうです。

ちなみにデスクトップガジェットはたとえIE9をインストールしてもHTML5コンテンツは動きませんし、JavaScriptもチャクラではなくJScript5.8で動きます。この時点でガジェットの未来はなさそうだと踏んでいましたが、思ったより早い終焉を迎えるようです。

Metro Style AppはたしかにWinRT上でJavaScript+HTML5+CSS3で開発することができ、ガジェットで培われたノウハウの一部は流用できる可能性はあるものの、ガジェットから単純に移行というのは難しそうです。利用者にとってもガジェットをデスクトップに常時複数表示させておき、作業中にもほかの情報を参照できるメリットが失われるのは厳しいものがあるように思います(Metro Style Appは一つだけクラシックデスクトップと同時に表示できる)。

告知も私の知るかぎりなかったですし、気づけばいきなり消滅していたという感じで、お困りの方も今後増えそうです(たしかにガジェットはいまいち流行ってなかったですがいきなりはヒドイ)。とりあえず現在追加インストールしているガジェットは、今後入手困難になる可能性が高いので、各自でバックアップを取っておくことをお勧めします。

.gadgetファイルそのものを保存していなくても、C:\Users\ユーザー名\AppData\Local\Microsoft\Windows Sidebar\Gadgetsの各サブフォルダがガジェット一つ一つに対応しているので、これをバックアップしておけば問題ありません。再インストールも単にこれらのファイルを同じ場所に書き戻すだけでOKです。

また、.gadgetファイルは単に関連ファイルをzipで固めたものなので、ご自分でこれらの各サブフォルダをzipにして.gadgetにリネームすればインストーラーを復元することもできます。

これらの措置は自己責任にてお願いします。各ガジェット作者のアナウンスがある場合はそちらに従ってください。

それにしても、WindowsデスクトップにHTML+スクリプトで記述されたミニプログラムを配置するといえば古くは「アクティブデスクトップ」まで遡ることになると思いますが、「ガジェット」で安定するかと思ったら二世代しか持ちませんでしたね。競合するGoogleデスクトップも終了しましたし、あまりウケがよろしくないんでしょうか。Metro Style Appはその点どうなんでしょうね?

元記事:http://blogs.wankuma.com/mutaguchi/archive/2011/10/09/204219.aspx

次のページへ

Copyright © 2005-2016 Daisuke Mutaguchi All rights reserved

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

Awards

Books

Twitter