2016/04/23

Japan PowerShell User Group (JPPOSH) 主催の第 6 回 PowerShell 勉強会(4/9)には多数の方にお越しいただき、ありがとうございました。

PowerShell勉強会は今後も年2回くらいのペースで続けて行きたいと思っていますので、どうぞよろしくお願い致します。

さて、私のセッション「PowerShell 5.0 新機能と関連OSSのご紹介」のスライドを公開します。前半は以前のものとだいたい同じですが、正式版対応版にアップデートしています。

今回は去年から今年にかけて、PowerShell関連ソフトウェアとしてOSS化したものを、まとめて紹介しました。以下は今回紹介したもののリストです。

またデモで用いたサンプルファイルも公開します。

このzipにも同梱してますが、PSScriptAnalyzerのカスタムルールはこんな感じで作ります。作り方は、ASTを受け取って、中身をチェックして、ルールに該当するならDiagnosticRecordを返すというのが基本になります。

using namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
using namespace System.Management.Automation.Language

Import-Module PSScriptAnalyzer

function Test-UsingVarsWithNonAsciiCharacter
{
    # 変数に半角英数字以外の文字種が含まれていると警告するカスタムルール。
    [CmdletBinding()]
    [OutputType([DiagnosticRecord[]])]
    Param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [ScriptBlockAst]
        $ScriptBlockAst
    )

    Process
    {
        [Ast[]]$variableAsts = $ScriptBlockAst.FindAll({
            param([Ast]$ast)
            $ast -is [VariableExpressionAst]
        }, $true)

        $variableAsts | 
        where {
            $_.VariablePath.UserPath -notmatch '^[a-zA-z0-9_]+$'
        }|
        foreach {
            $result = [DiagnosticRecord[]]@{
                "Message"  = "変数 `$$($_.VariablePath.UserPath) に半角英数字以外の文字種が使われています。"
                "Extent"   = $_.Extent
                "RuleName" = "AvoidUsingVarsWithNonAsciiCharacter"
                "Severity" = "Warning"
            }
            $result
        }
    }
}
Export-ModuleMember Test-UsingVarsWithNonAsciiCharacter

ついでにPesterのサンプルコードも。2つのパラメータを足し算する関数、Invoke-Additionに対するテストコードの例となります。

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

Describe "Invoke-Addition" {   # テストの定義
    Context "足し算の実行" {   # テストのグループ化
        It "整数値を2個指定すると、足し算された結果が返る" {  # テストケース
            Invoke-Addition 3 5 | Should Be 8 # アサーション
        }

        It "小数値を2個指定すると、足し算された結果が返る" {
            Invoke-Addition 3.4 5.8 | Should Be 9.2
        }
    }

    Context "エラーの発生" {
        It "足し算できないものを指定するとエラー" {
            {Invoke-Addition 10 "x"} | Should Throw 
        }
    }
}

2014/12/24

はじめに

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

今回は、Windows 8から追加されたOSの機能である、「Windows 位置情報プラットフォーム」をPowerShellから呼び出して、位置情報(緯度、経度)を取得してみよう、という話になります。

Windows 位置情報プラットフォームとは

Windows 8から、「Windows 位置情報プラットフォーム」という機能が追加され、アプリケーションから現在位置情報(緯度、経度など)をAPIで取得できるようになっています。

Windows 位置情報プラットフォームでは、位置情報をGPSがあればGPSから、なければWi-Fiの位置情報あるいはIPアドレスなどから推定して取得します。すなわち、GPSがない場合でも位置情報を取得できる、いわば仮想GPSの機能がデフォルトで備わっているのがミソです。

(注:Windows 7にも「Windows センサー&ロケーションプラットフォーム」というのがありましたが、OSデフォルト機能としては仮想GPSはありませんでした。今は亡き、Geosense for Windowsというサードパーティー製アプリを追加すると仮想GPS使えたんですけどもね。あとWindows Phone?知らない子ですね…

PowerShellでWindows 位置情報プラットフォームを利用する

さて、Windows 位置情報プラットフォームをPowerShellで使うには、.NET4.0以上に含まれている、System.Device.Location名前空間配下に含まれるクラスの機能を用います。アセンブリ名としてはSystem.Deviceとなります。

以下のような関数Get-GeoCoordinateを定義します。

Add-Type -AssemblyName System.Device

function Get-GeoCoordinate
{
    param(
        [double]$Latitude,
        [double]$Longitude
    )

    if(0 -eq $Latitude -and 0 -eq $Longitude)
    {
        $watcher = New-Object System.Device.Location.GeoCoordinateWatcher
        $sourceId = "Location"
        $job = Register-ObjectEvent -InputObject $watcher -EventName PositionChanged -SourceIdentifier $sourceId
        $watcher.Start()
        $event = Wait-Event $sourceId
        $event.SourceEventArgs.Position.Location
        Remove-Event $sourceId
        Unregister-Event $sourceId
    }
    else
    {
        New-Object System.Device.Location.GeoCoordinate $Latitude,$Longitude
    }
}

関数実行前に、まずAdd-Type -AssemblyName System.Deviceを実行して必要なアセンブリをロードする必要があります。

関数本体ではまず、System.Device.Location.GeoCoordinateWatcherオブジェクトを生成します。このオブジェクトのStartメソッドを実行すると、Windows 位置情報プラットフォームにアクセスして、位置情報の変化を監視します。位置情報の変化を感知すると、PositionChangedイベントが発生し、取得した位置情報を、イベントハンドラの引数にGeoPositionChangedEventArgs<T>オブジェクトとして返します。

さて、PowerShellでは、.NETクラスのイベントを取得するには、Register-ObjectEventコマンドレットを用い、イベントを「購読」します。

イベントが発生するたびに何かの動作をする、というような場合では、Register-ObjectEvent -Action {処理内容}のようにして、イベントハンドラを記述するのが一般的です。が、今回は位置情報の変化の最初の一回(つまり、初期値の取得)さえPositionChangedイベントを捕まえればOKなので、-Actionは使用しません。

代わりにWait-Eventコマンドレットを用い、初回のイベント発生を待機するようにしています。Wait-Eventコマンドレットは、当該イベントを示すPSEventArgsオブジェクトを出力します。

PSEventArgsオブジェクトのSourceEventArgsプロパティには、当該イベントのイベントハンドラ引数の値(ここではGeoPositionChangedEventArgs<T>オブジェクト)が格納されているので、あとはそこから.Position.Locationと辿ることで、位置情報を格納したGeoCoordinateオブジェクトが取得できます。

(注:あとで知ったんですけど、GeoCoordinateWatcherクラスには、同期的に位置情報を取得するTryStartメソッドというのがあって、これを使えばイベント購読は実は不要でした…まぁいっか)

なお、関数のパラメータとしてLatitude(緯度)、Longitude(経度)を指定すると、現在位置ではなく、指定の位置を格納したGeoCoordinateオブジェクトを生成するようにしています。

Get-GeoCoordinate関数の使い方

事前にコントロール パネルの「位置情報の設定」で「Windows 位置情報プラットフォームを有効にする」にチェックを入れておきます。

あとはGet-GeoCoordinateをそのまま実行するだけです。

Latitude           : 34.799999
Longitude          : 135.350006
Altitude           : 0
HorizontalAccuracy : 32000
VerticalAccuracy   : NaN (非数値)
Speed              : NaN (非数値)
Course             : NaN (非数値)
IsUnknown          : False

このように現在位置が表示されるかと思います。といっても、緯度、経度が表示されたところでちゃんと取得できてるのかよく分からないので、以下のような簡単な関数(フィルタ)を定義しておきます。

filter Show-GoogleMap
{
    Start-Process "http://maps.google.com/maps?q=$($_.Latitude),$($_.Longitude)"
}

このフィルタを使うと、指定の緯度経度周辺の地図を、標準のWebブラウザで開いたGoogleマップ上に表示してくれます。使い方はこんな感じ。

Get-GeoCoordinate | Show-GoogleMap

現在位置が表示されましたでしょうか? 位置測定に用いたソースによってはkmオーダーでズレると思いますが、それでも何となく、自分がいる場所が表示されるのではないかと思います。

なお、先ほども書いたように、パラメータで任意の緯度、経度を指定することも可能です。この関数だけではあんまり意味を成しませんが…

Get-GeoCoordinate 35.681382 139.766084
まとめ

PowerShellでも「Windows 位置情報プラットフォーム」を使って現在位置が取れるよ、という話でした。あんまりPowerShellでSystem.Device.Locationとかを使っているサンプルを見かけないので、何かの参考になれば幸いです。あとPowerShellでのイベントの扱い方についても復習になるかと。

ところでこうやって取得した位置情報を使って、Web APIを呼び出して活用しよう、というようなネタを書くつもりだったんですが、長くなったんでまたの機会としましょう。ではでは。

2014/03/23

昨日は、MVP Community Camp 2014の大阪会場にお越しいただき、ありがとうございました。

私のセッションスライドを公開します。

前回のエントリーでも書きましたが、DSCについては何度かセッションをやってきており、今回が一応の集大成的なものとなるかと思います。

今回はDSCというPowerShell4.0で追加された応用的な内容でしたが、4/12に開催されるPowerShell勉強会@大阪では基礎的なところからセッションをやる予定です。PowerShell勉強会@大阪については後ほど詳しく紹介エントリーを上げます。

さて、今回デモをやろうとしたんですが、見事に失敗してしまいました。大変申し訳ありません!

原因としてはHyper-Vにドメインコントローラー(兼、Configuration配布用サーバー)と設定対象サーバーの2台を同一ドメインに所属させていたのですが、手違いで設定対象サーバーからDCへの認証ができなくなっていて、結果、DSC反映時に必要なKerberos認証に失敗したためでした。やはりDCを仮想化させるのはいくらHyper-V 3.0でも注意深くやらないとダメですね。直前まではうまく動いてたんですけどもね…DCはやはり独立して用意すべきでした

本番ではデモに失敗したんですが、環境を再整備して動作することを確認しました。そこで、今回、デモで用いる予定だったスクリプトを一式、公開する事にしました。こちら:dsc_demo.zip

使用方法を以下に説明します。なお、すべては無保証なので、必ず、壊れてもいいサーバーを新規で用意してください。また、デモ環境をHyper-Vの仮想サーバーとしておくと、DSC適用前の状態のスナップショットを取れるのでいつでもデモ実行前の状態に戻せて便利です。

事前準備
  1. Configuration配布用サーバー(コンピューター名:dscpull)、設定対象サーバー(コンピューター名:target)を用意し、双方にWindows Server 2012 R2をGUI有効にしてインストールする。
  2. Windows Server 2012 R2がRTM版である場合は、General Availability Update Rollupを適用してGA相当にアップデートする(ビルド番号6.3.9600.16394の状態にする)。
  3. 両サーバーを同一ドメインに所属させる。
  4. 両サーバー間でKerberos認証が通りWinRMでリモート接続できることを確認しておく。
    たとえば、dscpullサーバーからEnter-PSSession targetとしてtargetサーバーにリモート接続できるかどうかで確認できます。
  5. dsc_demo.zip中に含まれるdscpullフォルダをConfiguration配布用サーバーのC:\直下にコピーする。
  6. dsc_demo.zip中に含まれるtargetフォルダを設定対象サーバーのC:\直下にコピーする。
Pushモードのデモ

このデモでは、dscpullサーバーでDSCをpushモードで実行することにより、「targetサーバーにIISをインストールし、Webサイトを作成し、開始する」という操作を適用します。よって、事前にtargetサーバーにはIISが入っていないことを確認しておいてください。

  1. targetサーバーのInstall_Website_Resource.ps1を実行する。
    この操作により、xWebAdministrationリソースモジュールがtargetサーバーに配置されます。
  2. dscpullサーバーのStart_Website_of_Target.ps1を実行する。
    この操作により、xWebAdministrationリソースモジュールがdscpullサーバーにも配置され、xWebSite等のリソースを呼び出すConfigurationに従ってMOFファイルを生成し、Start-DscConfigurationコマンドレットによって実際にMOFファイルの内容をtargetサーバーに反映させます。
    ※Pushモードの場合、カスタムリソースを利用する際は、実行側と対象の双方にカスタムリソースの事前配置が必要です。
  3. Start_Website_of_Target.ps1の実行が終了したら、targetサーバーをチェックして下さい。IISがインストールされ、IISマネージャ上ではDefault Web Siteは停止状態となり、MyWebSiteというサイトが作成され開始されていると思います。http://target/を開いてみてください。
  4. targetサーバーの設定を色々と変更して(Webサイトを停止する、削除する、IISをアンインストールする、InetPubフォルダのコンテンツを削除するetc)、再度Start_Website_of_Target.ps1を実行した場合でも、同じ設定に戻ることを確認してください。
Pullモードのデモ

このデモではまずdscpullサーバーにDSC Serviceをインストールし、PullサーバーとしてMOFファイルを配布できるようにします。続いてtargetサーバーの設定をPullモードにし、dscpullサーバーから設定を定期的に取得、反映させるようにします。(このデモでは「印刷サービス」(プリントサーバー)をインストールするConfigurationをサンプルとして利用しています)

  1. dscpullサーバーのInstall_DSC_Service.ps1を実行する。
    この操作により、xPSDesiredStateConfigurationリソースモジュールがdscpullサーバーに配置され、このモジュールに含まれるxDscWebServiceリソースを呼び出すConfiguration(Sample_xDscWebService)を実行し、Pullサーバーが構築されます。
  2. dscpullサーバーのDeploy_Config.ps1を実行する。
    この操作により、プリントサーバーをインストールするConfigurationからMOFファイル(ファイル名は対象サーバー名ではなく、対象サーバーを識別するConfigurationIDとなる)を生成し、New-DscCheckSumコマンドレットによりチェックサムファイルを出力し、両ファイルをPullサーバーのMOFファイル配布用フォルダにコピーすることで、設定の配置が完了します。
  3. targetサーバーのConfig_LCMforPull.ps1を実行する。
    この操作により、LCM(Local Configuration Manager)設定用のConfigurationからMOFファイルを生成し、Set-DscLocalConfigurationManagerによりMOFファイルを反映し、targetサーバーのLCMがPushモードからPullモードに変更されます。また対象サーバーを識別するためのConfigurationIDも定義します。このスクリプトを実行すると処理の最後で自動的に再起動を実行します。(DSCのモード切替は再起動後に反映されます)
  4. 再起動後、targetサーバーに印刷サービスがインストールされていることを確認してください。上手くいかない場合は、イベントビューアで”Desired State Configuration”を確認してください。またLCMのConfigurationに指定した間隔で設定が再反映されていること、あるいは新設定をPullサーバーに取得しにいっていることを確認してください。

2013/12/05

はじめに

この記事はPowerShell Advent Calendar 2013の5日目の記事です。

突然ですが、PowerShellにはJobが完了するまで待機するWait-Jobコマンドレットというのがあります。これはその名の通り、パイプラインから入力したJobオブジェクトがすべて(あるいはどれか一つが)完了状態になるまでスクリプトの実行を待機する効果があります。

当然ながらWait-JobはJobオブジェクトにしか利用できませんが、任意の入力オブジェクトに対して待機条件を指定してやれば、その条件を満たすまで実行を停止するコマンドがあると便利なんじゃないかな?と常々思っていたので書いてみました。

Wait-State関数
function Wait-State
{
    [CmdletBinding(DefaultParameterSetName="ByProperty")]
    param(
        [Parameter(ValueFromPipeline=$true)]
        [PSObject]$InputObject,
        [Parameter(Position=1,Mandatory=$true,ParameterSetName="ByProperty")]
        [string]$Property,
        [Parameter(Position=2,ParameterSetName="ByProperty")]
        [object]$Value,
        [Parameter(Position=1,Mandatory=$true,ParameterSetName="ScriptBlock")]
        [Alias("Script")]
        [ScriptBlock]$FilterScript,
        [Parameter()]
        [switch]
        $Any,
        [Parameter()]
        [switch]
        $IgnoreImmutable,
        [Parameter()]
        [switch]
        $PassThru,
        [Parameter()]
        [switch]
        $AllOutput,
        [Parameter()]
        [int]
        $IntervalSec=1,
        [Parameter()]
        [int]
        $TimeoutSec=60
    )

    begin
    {
        $objects = @()
        $watch = New-Object System.Diagnostics.StopWatch
        $watch.Start()
        $firstChecked = $false
    }

    process
    {
        foreach($o in $InputObject)
        {
            $objects += $o
        }
    }

    end
    {
        while($true)
        {
            $remains = @()
            foreach($o in $objects)
            {
                if($firstChecked)
                {
                    if($o.Refresh)
                    {
                        $o.Refresh()
                    }
                }

                if($null -ne $FilterScript)
                {
                    if($o|&{process{&$FilterScript}})
                    {
                        if($PassThru)
                        {
                            if((!$IgnoreImmutable -or ($IgnoreImmutable -and $firstChecked)))
                            {
                                $o
                            }
                        }
                    }
                    else
                    {
                        $remains += $o
                    }
                }
                else
                {
                    if($Value -eq $o.$Property  -and (!$IgnoreImmutable -or ($IgnoreImmutable -and $firstChecked)))
                    {
                        if($PassThru)
                        {
                            if((!$IgnoreImmutable -or ($IgnoreImmutable -and $firstChecked)))
                            {
                                $o
                            }
                        }
                    }
                    else
                    {
                        $remains += $o
                    }
                }
            }

            if($remains.Length -eq 0)
            {
                break
            }
            elseif($Any -and $remains.Length -lt $objects.Length)
            {
                if($AllOutput -and $PassThru)
                {
                    $remains
                }
                break
            }
            elseif($watch.Elapsed.TotalSeconds -ge $TimeoutSec)
            {
                if($AllOutput -and $PassThru)
                {
                    $remains
                }
                break
            }
            
            $objects = @($remains)
            $remains = @()
            
            $firstChecked = $true

            Start-Sleep -Seconds $IntervalSec
        }
    }
}
コマンド構文
Wait-State [-Property] <string> [[-Value] <Object>] [-InputObject <psobject>] [-Any] [-IgnoreImmutable] [-PassThru] [-AllOutput] [-IntervalSec <int>] [-TimeoutSec <int>]  [<CommonParameters>]

Wait-State [-FilterScript] <scriptblock> [-InputObject <psobject>] [-Any] [-IgnoreImmutable] [-PassThru] [-AllOutput] [-IntervalSec <int>] [-TimeoutSec <int>]  [<CommonParameters>]
パラメータ

-InputObject:入力オブジェクト。パイプライン入力可。
-Property:変更を確認するプロパティ名。
-Value:-Propertyで指定のプロパティ値が、このパラメータに指定する値になるまで待機する。
-FilterScript:プロパティを指定する代わりに待機条件をスクリプトブロックで指定する。
-Any:入力のどれか一つが条件を満たすまで待機するようにする。(省略時は入力が全部条件を満たすまで待機)
-PassThru:入力オブジェクトが待機条件を満たした時点で、そのオブジェクトを出力する。省略時は出力なし。
-IgnoreImmutable:最初から条件を満たしている場合は出力しない。-PassThruと併用。
-AllOutput:タイムアウトした場合や-Any指定時に一部のオブジェクトしか出力していない場合でも、最終的に未出力のすべてのオブジェクトを出力してから終了する。-PassThruと併用。
-IntervalSec:プロパティ値のチェック、もしくは待機条件スクリプトの実行の間隔秒数を指定。デフォルト1秒。
-TimeoutSec:最大待機秒数。デフォルト60秒。この時間を過ぎると条件を満たしていなくても待機を終了する。

使用例
# 停止しているサービスがすべて開始するまで待機する。
Get-Service |? Status -eq Stopped | Wait-State -Property Status -Value Running

# 上記と同じだが、開始したサービスを逐次表示する。
Get-Service |? Status -eq Stopped | Wait-State -Property Status -Value Running -PassThru

# 停止しているサービスが少なくとも1つ開始するまで待機する。
Get-Service |? Status -eq Stopped | Wait-State -Property Status -Value Running -Any

# プロセスのワーキングセットが100MBを超えた段階で逐次表示する。
Get-Process | Wait-State {$_.WorkingSet -ge 100MB} -PassThru

# 上記と同じだが、最初から100MBを超えてるものは出力しない。
Get-Process | Wait-State {$_.WorkingSet -ge 100MB} -PassThru -IgnoreImmutable

# ディレクトリ内のファイル容量がすべて50KBを超えるまで待機し、出力のFileInfo配列を変数に代入。
$files = Get-ChildItem | Wait-State {$_.Length -ge 50KB} -PassThru -AllOutput -TimeoutSec 3600
問題点

プロパティ値を取得するときにリアルタイムに値が反映されないオブジェクト(要するにGetした時点のプロパティ値がずっと固定されてるもの)に対しては正しく動作しません。というか、PowerShellで扱うオブジェクトはほとんどそうなんじゃないかと思います(汗

ServiceControllerオブジェクト、Processオブジェクト、FileInfoオブジェクト、DirectoryInfoオブジェクトについては、Refreshメソッドを実行すると、プロパティ値を現在の値に更新してくれるので、それを利用してプロパティ値を監視できるようにはしています。

それ以外についても監視できるようにするには、たぶんそれぞれのオブジェクトに応じた監視方法を地道に調査して実装していくしかないんじゃないかなあと思います。

INotifyPropertyChangedインターフェースを実装したクラスについては、PropertyChangedイベントをSubscribeしてプロパティ値の変更を追跡できるようにしてみようとちょっと思ったんですが、PowerShellで扱うオブジェクトにINotifyPropertyChangedを実装したクラスのものってそんなにあるんだろうか?と疑問を覚えたのでやめました。

WMIオブジェクトについては何か共通の方法でプロパティ値変更を監視できないかなあと思ったんですが、結局IntervalSec間隔でクエリを発行する方法になってしまい、低コストで行う方法がちょっと思いつきませんでした。

ただ、-FilterScriptパラメータをサポートしているので、ここに書くことでいかようにも待機条件をカスタマイズできるので、極端な話、条件スクリプトブロックに{(Get-Hoge -Name $_.Name).Property -eq “ほげ”}みたいなコードを書いてゴリ押しすることもできるかと思います。

感想

というわけで、なんだか微妙な成果になって恐縮ですが、なんで無いんだろうと思っていた関数を実際に書いてみると、無い理由が分かったりするものなんだなあ、と思ったりした次第です。

スクリプトの解説を何もしてないですが、あえて解説する程のものでもないこともないですが、まあ長くなるのでやめときます。

ただ、入力オブジェクトを一旦全部取得してから、後続パイプラインに流し込む例としていくらか参考になるかもしれません。(beginで入れ物を用意して、processで詰めて、endでメインの処理を書くだけですけど)

あとはフィルタースクリプトブロックの実装方法の一例としても参考になるかも? スクリプトブロックを二重にして$_に対象オブジェクトがきちんと格納されるようにする方法、若干トリッキーな気もしますが正式にはどう書くのが良いのか不明なのでこうしてみました。

2013/07/02

というわけで、10年目に突入しました。これも私のアウトプットをインプットしてくださる皆様、私のインプットとなるアウトプットをしていただいている皆様のおかげです。いつもありがとうございます。

10周年なので軽く歴史を振り返ります。

2004年にWSH Lab.というサイトを中心としたWSH / VBScript関係の活動が評価されてfor Developer - Scriptingという分野でMSMVPを受賞したのがはじまりです。

実はWSH Lab.を始めたのが1999年のことで、それ以前も1997年頃からWindows系のニュースグループに出入りして、オンラインコミュニティではMVP受賞前も知る人ぞ知る存在ではあったように思います。(なんせ、WSHは相当ニッチな分野だったもので、まともに取り扱おうと思う人は当時はほとんどいなかったもので)

当時は私はまだ大学生(しかも分野は化学系でIT関係なし)でして、体調崩したり色々あって卒業後も就職せず迷走してた時期でした。なのでIT系の方々とも交流はなく、オンラインのみで何故かWSHというこれまたあんまり誰も手を付けないマイナー分野で突っ走ってる謎の存在だったんじゃないかなーと思います。

MVP受賞後はひょこひょこオフラインにも出没するようになり、謎めいた存在から確かに実在する存在として認識されていったのではないかと思います。2006年にはわんくま同盟に加入し、IT系の方々と交流し、また自らセッションをする機会にも恵まれました。

MSMVP受賞のおかげで2006年からは@ITさんの方でチェック式 WSH入門という連載をさせていただくことができ、商業デビュー(?)を果たしました。

だんだんWSHがフェードアウトしていくと同時にPowerShellというものが登場したのも2006年のことでした。当時は.NETのことは全然知らなかったのですが、このままでは取り残される!と思い、それなりに勉強を始めたものでした。受賞カテゴリも2007年からはfor Data Center Management- Admin Frameworksというよく分からない名前に代わり、開発系からサーバー系への移籍となりました。

もちろんWindows Serverなんてものもまともに触ったこともなく、今思えばあの頃は色々と憶えることがたくさんあったなーと思いますね。そんなこんなで2008年にはMVPカテゴリもfor Data Center Management- PowerShellとなり、PowerShell専門となりました。

2008年には技術評論社さんからWindows PowerShell ポケットリファレンスという書籍を書かせてもらうことができました。まさか自分が本を書くなんて思ってもいなかったですし、あれはいい経験になったと思っています。

それからも体調はずっとよくなくて迷走しまくりで随分多くの方々にご迷惑をかけ続けていましたが、なんとかMVPという繋がりを武器に社会と繋がっていようとあがいた感じです。書籍や記事執筆、スピーカー、そして密かにプログラマーとして会社に勤めたりしてた時期もありました。

私のあがきとは関係なく、PowerShellはどんどん重要性をましていって、特にWindows Server 2012とPowerShell 3.0がリリースされた2012年は全国各地で声がかかって(自分で志願したのもありますが)、計8回もセッションを担当しました。中でもMicrosoft Windows Developer DaysでMicrosoft社員さんに交じってセッションを担当したのは貴重な経験でした。そして今年はPowerShellポケットリファレンスの改訂版を世に出すことができました。

こうやって10年目にして振り返ると、確かにWindows Scripting / PowerShell周りで要所要所でいろいろやってきたなーという感じですね。今年もPowerShell 4.0 / Windows Server 2012 R2がリリースされる予定で、PowerShellは今後も着実に発展、浸透していくのだと思います。私も陰ながらと言わず割と表立って、そのお手伝いをしていければいいなーと思っています。

さて余談ですが、結局あがいた結果今はどうなん?という話なんですが、結果としてはITの世界では今まで通り、PowerShellの分野ではこういう感じの活動を続けてますが、メインのおしごとは全然ITとも化学とも違う別なことをやっていたりします。ちゃんとおしごとしてるのでそんな目で見ないでくださいね。あと、体調に関しては2年前くらいからほぼ完全に回復してるので、遠慮せず遊んでやってください。

こんなことを書いてると、なんか「お前…消えるのか…?」と思われてしまうかもしれませんがそんなことは全然ない(と思う)ので、これからも変わらず、お付き合いいただければと思います。

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

PowerShell 2.0のXP/Vista/2003/2008用の正式版が10/26にリリースされました。

Description of the Windows Management Framework on Windows XP, Windows Server 2003, Windows Vista, and Windows Server 2008
http://support.microsoft.com/kb/968929/en-us

結局、日本語版のインストーラーがどこにあるのか分かりにくいので直リンクをどうぞ。

2008 x86
2008 x64
2003 x86
2003 x64
Vista x86
Vista x64
XP

パッケージに関して、くわしくはこちらの記事を。

Windows PowerShell Blog : Windows Management Framework is here!
http://blogs.msdn.com/powershell/archive/2009/10/27/windows-management-framework-is-here.aspx

Shigeya Tanabe's blog : XP, Vista, Windows Server 2003, 2008 向けの Windows Powershell 2.0 が公開されました
http://blogs.technet.com/stanabe/archive/2009/10/28/powershell-for-xp-vista-ws03-ws08-released.aspx

さて、PowerShell 2.0は1.0と同居できないため、1.0がインストールされている環境だとインストーラーではねられます。まずは1.0をアンインストールしてください。2008では機能の削除、2003などではプログラムの追加と削除から、「更新プログラム」チェックをつけて「kb926140」を削除してください。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/10/28/182502.aspx

2009/08/15

イマイチ注目度の低いガジェットですが、Windows Vista→7でかなり変更があります。まだ開発までは追いきれてないのですが、とりあえず使用方法について気付いたことをメモ。

  Windows サイドバー ガジェット Windows デスクトップ ガジェット
OS Windows Vista Windows 7
デフォルト表示位置 デスクトップの左右どちらか(マルチディスプレイ環境ではいずれか一つのディスプレイ) デスクトップの任意の場所。ただし、デスクトップの左右には目に見えないグリッドがあり、マウスでD&Dすると位置補正がかかる。(シフトキーを押しながらだと補正はかからない)
整列 左右に格納(Docked)時は自動整列 自動整列なし
マルチページ 左右に格納時、はみ出したガジェットは2ページ目以降に表示される マルチページ機能なし
ショートカットキー

Windowsキー+スペースキーで手前に表示
Windowsキー+Gでガジェットの選択

Windowsキー+Gでガジェットの選択
のみ (8/19修正。JZ5さん komvoyさんありがとうございます)

ガジェットの大きさ Docked時は小さいサイズ、unDocked(デスクトップの任意の場所に配置)時は大きいサイズ ガジェットの右上に表示されるボタンで小さいサイズと大きいサイズのいずれかを選択できる
ガジェットの最小サイズ 130 x 55px なし?
起動方法 スタートメニュー→「すべてのプログラム」→「アクセサリ」→「Windows サイドバー」から起動 デスクトップを右クリックし、「表示」→「デスクトップ ガジェットの表示」にチェック
終了方法 通知領域(タスクトレイ)のアイコンの右クリックメニュー デスクトップを右クリックし、「表示」→「デスクトップ ガジェットの表示」のチェックをはずす
ガジェットの追加 サイドバーの上の+ボタンクリック デスクトップを右クリックして「ガジェット」をクリック
なんか、微妙に機能が削られてるというかなんというか…あと、デスクトップ右に並べた時に右にできるスペースが妙に広い。まぁアイコンが大きくなったせいなんでしょうが… 元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/08/15/180164.aspx

2009/06/08

むたぐちです。いろいろあってご無沙汰になっていますが、ぼちぼち復活中です。

1年間更新が途絶えていたWSH連載(@IT)が再開になりました。遅くなってすみませんです。

チェック式 WSH入門
第18回 FileSystemObjectオブジェクトを利用する(3)
http://www.atmarkit.co.jp/fwin2k/tutor/cformwsh18/cformwsh18_01.html

懲りずにネタを仕込んでますが気づいた方はしれっと流してくださいw

この連載記事も次回で最終回です。 @ITのWindows Server Insiderフォーラムではいつも上位PVをキープしているようでありがとうございます。

http://www.atmarkit.co.jp/fwin2k/

また、Windows 7とWindows Server 2008 R2の標準機能(コマンドプロンプトと同様に削除もできない)となるPowerShell ver2についていち早く紹介するセッションを7/4(土)わんくま大阪勉強会でやります。ぜひご参加ください!

http://www.wankuma.com/seminar/20090704osaka30/Default.aspx

おそらく、7を入れてみて多くの人が思うことは、まずタスクバーが分からないってのと、スタートメニューのアクセサリにあるPowerShellってなんぞ?ってなると思うんですよね。タスクバーはタスクバーのプロパティを適当に設定すればVistaライクになりますが、PowerShellはいきなり見てもなんだか分からないと思いますので、基礎をざっとやって、あとはver1からの変更点をお話します。

あと6/13わんくま大阪もスタッフとして参加しますのでよろしくですv

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/06/08/174534.aspx

2008/11/22

ご無沙汰してます。牟田口です。近況は省略(えー mixiついったーブログその他などを。

さて、最近、spamコメントが鬱陶しくて色々対策を考えてるんですが、手っ取り早く効果的なのはBBQを使うことですね。某巨大掲示板群サイトで荒らしに使われた、おもに公開プロキシのリストをDNSを引いて持ってくるというものです。Perlの実装はこのページにあります。PHPの実装も見かけました

意外と.NET,C#な実装を見かけないので、最近覚えたC#でさくっとかいてみました。

using System;
using System.Net;
using System.Net.Sockets;

namespace CheckBBQ
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("BBQ判定プログラムの使い方:\r\ncheckbbq.exe IPAddressもしくはHostName\r\n戻り値:\r\n-1:失敗\r\n0:串ではない\r\n1:串である");
                return;
            }
            IPAddress[] addresses= Dns.GetHostAddresses(args[0]);
            if (addresses.Length == 0)
            {
                Console.WriteLine("-1");
                return;
            }
            string ipAddress = addresses[0].ToString();
            string[] ipAddressParts = ipAddress.Split('.');
            Array.Reverse(ipAddressParts);

            string hostName = string.Join(".", ipAddressParts) + ".niku.2ch.net";
            try
            {
                addresses = Dns.GetHostAddresses(hostName);
            }
            catch (SocketException ex)
            {
                Console.WriteLine("0");
                return;
            }
            catch (Exception ex)
            {
                Console.WriteLine("-1");
                return;
            }

            if (addresses.Length == 0)
            {
                Console.WriteLine("-1");
                return;
            }

            ipAddress = addresses[0].ToString();
            if (ipAddress == "127.0.0.2")
            {
                Console.WriteLine("1");
            }
            else
            {
                Console.WriteLine("-1");
            }
        }
    }
}

使い方はコードを見てください。これは単に引数のホストをBBQにかけ、結果を標準出力に出すコンソールアプリですが、メソッド化してもaspxに組み込んでもWebサービスにしてもまぁ好きなように使ってくださいませ。

でも、BBQは万能選手じゃありません。本来書き込めるべき人が書き込めないことも多々あります。なので、BBQをまず通してOKならOK、NGならCAPTCHA認証をやってもらう、とかがいいと思います。

トラックバックspam対策もいろいろ考えたんですが、まだ国産のは少ないので、送信データが英字のみをはじく、でも効果は結構あります。このへんmixiでメモったのでコピペ。

トラックバックって
2008年10月11日20:04

黒歴史になるんだろうか
オートディスカバリーはないと微妙だしあるとspamの温床になるし。
何らかの認証の共通規格を設ける?
RSSは2.0で一応落ち着いたけどトラックバックは発展しないままだなー
私はいま、現行規格のまま、オートディスカバリーを有効にしてかつspamトラックバックが送られないようにする方法を考え中
送り元を見に行くというはてな方式も一つの方法論であるんだけど、なんかこう納得できないものがある

コメント
むたぐち 2008年10月11日 20:07
送信元ホワイトリスト方式もなんだか微妙です
むたぐち 2008年10月11日 20:18
excerpt,titleなどの文字列で弾くのはよくある対策だけど根治法じゃない
いたちごっこだー
BBQは使えない。理由は少し考えれば分かりますので略。
やっぱりはてな方式なんかな。urlの先をまず見に行って、そこにこちらへのリンクがなければまず速効NG。
あとはお好みで、引用がなければNGとか。
でもこれって莫大なコストがかかるし送り元を見に行くというのがそもそもなんか根本的にどうなんっておもう。
むたぐち 2008年10月11日 20:38
トラックバックは性善説ベースで作られた規格
引用元を見に行くのは性悪説ベースの対処
両極端すぎる
むたぐち 2008年10月11日 21:08
送り元を見に行く方式の問題はまだあって、A→Bにトラックバックを送信された場合、B→Aに「AにBのURLが存在するか」を確認しに行く。
一見合理的だけど、このトラックバックって誰でも送れるんだよねー。Aを書いた人じゃなくても。つまり、AともBとも関係ない悪意を持つxがいて、A→Bへのトラックバックを乱射した場合どうなるか。B→AのDoS攻撃みたいなんが成立しちゃう。同ドメインへの確認間隔を制御する必要がでてくる。でもそれはもちろん正しくサービスを利用する人にとっては不便になるわけで。
むたぐち 2008年10月11日 21:28
送信元を見に行く方式で、トラックバックを送った人のリモートホストと、urlに指定されたWebサイトのドメインが一致するかまず調べるという方法もあるけど(たぶんはてなではこれをやっている)、これは正しい使い方をしていても一致しないことも当然あるわけで(手動でトラックバック打つときとかね)。だけどこの辺が確かに手の打ちどころではあるようには思う。手動で打つ時はオートディスカバリー関係ないしな。ただ、私のようにDNSの逆引きができないというか正逆で結果が異なるようなサーバーの場合は泣いてもらうしかないかなー。
むたぐち 2008年10月11日 21:32
つまり、オートディスカバリー用(Blog提供サービスが見に行く用)のtrackback ping URLと、手動で打つ場合のtrackback ping URLをまず分ける。
前者は、トラックバックを送った人のリモートホストと、urlに指定されたWebサイトのドメインが一致するかまず調べ、一致しない場合ははじく。一致した場合は送信元を見に行って、こちらへのリンクがある場合は通す。それ以外ははじく。
後者は、trackback ping URLを用意するがあるところまではコピペできるが、それに数文字、画像にかかれた文字をつけたすようにする(認証の代わり)。
この辺が落としどころかなー。
むたぐち 2008年10月11日 21:35
追加文字列は定期的に変わるようにする。
むたぐち 2008年10月11日 21:37
オートディスカバリーのほうはチェック間隔に制限を設ける。
JZ5 2008年10月11日 22:38
SPAM温床はメールと同じようなもんじゃないだろか。
バーベキューってなんですか?
手動のトラックバックURL(使わないけど)は、JavaScriptなんかで生成するのはどう? Server側とClient側で共通のアドレス作れるけど、直接ファイル読み込んでJavaScript実行してないと正しいURLがない。
http://katamari.jp/blog/index.php?UID=1163941454
むたぐち 2008年10月11日 22:54
いえねー、トラックバックってわりと新しい規格なのになんでspam温床になることを考えて規格作らなかったのかと。
eメールは昔々作られたものだから仕方ないとして。
JavaScriptいいですねー。やってみます。
トラックバックspam対策って結局同じところに辿りつくのねw>URL
むたぐち 2008年10月11日 22:55
BBQについてはこちら
http://bbq.uso800.net/

JZ5さん事後承諾でごめんなさいだけどコピペさせていただきました。

あと、BBXを使うのも一つの案かな。こっちは広告爆撃ブラックリストなので近いものがあるかと。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2008/11/22/161939.aspx

次のページへ

Copyright © 2005-2016 Daisuke Mutaguchi All rights reserved

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

Awards

Books

Twitter