2015/12/21

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

PowerShellの属性

PowerShellには2.0から言語機能として「属性」機能が追加されました。PowerShellの属性はC#の属性とほぼ同じものですが、2.0〜4.0の時点では、高度な関数(Advanced Function)を作成するために関数に付与するCmdletBinding属性(記述上ではparamブロックに付与する形になる)、関数のパラメータに付与するParameter属性やAlias属性等、関数のパラメータや変数に付与する各種検証属性(Validate〜、Allow〜系)くらいしか使うことはなかったかと思います。

このうちパラメータ検証属性については、あえとす氏が以前詳しく解説しておられます。:パラメーターの検証属性について/前編 - 鷲ノ巣

PowerShellで属性クラスを作る

さて話は変わって、つい先日、WMF 5.0 / PowerShell 5.0がRTMしました。Windows 7/8.1、Windows Server 2008R2/2012/2012R2用のインストーラーが公開されたので、もう使っておられる方もいると思います。ぎたぱそ氏が一晩で解説記事を書いてくれてますね。

PowerShell 5.0では言語機能としてclass構文がついに追加されました。これでついにPowerShell言語も真の意味でオブジェクト指向言語の要素を完備したと言えるかと思います。

このclass構文、.NET Frameworkの既存のクラスを基底クラスとして継承するなんてことも、普通にできてしまいます。そこで私が考えたのは、もしかしてこれで属性も作れるんじゃない?ということでした。

v5のclass構文の基本的な部分の解説は、また別の機会にということにしまして、今回はいきなり、属性を作る話をしていきます。

ちなみにC#の属性については、属性 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C が参考になります。PowerShellで属性を作る時も基本はだいたい同じかと思います。

属性クラスの例

属性パラメータ(コンストラクタ引数)にDescription、名前付きパラメータとしてNoを指定でき、classに適用可能なTest属性はこんな感じです。

[AttributeUsage([AttributeTargets]::Class)]
class TestAttribute : Attribute
{
    [string]$Description

    [int]$No

    TestAttribute($Description)
    {
        $this.Description = $Description
    }
}

これを利用する際は、まずこのclassを含むスクリプトをドットソースで実行することで、グローバルに読みこむ必要があるようです(同一スクリプト内に属性定義と属性利用をまとめて書くとうまく動作しない)。

呼び出し側では以下のように指定します。

[TestAttribute("クラスの説明", No = 1)]
class Foo
{
    $X = 1
}

通常なら[Test()]のように"Attribute"の部分は省略できるはずなんですが、これも何故かフルネームで指定しないとダメなようでした。

クラスに属性が正しく指定されているかどうかはリフレクションで調べます。

[Attribute]::GetCustomAttributes([Foo])

結果は

Description  No TypeId
-----------  -- ------
クラスの説明  1 TestAttribute

こんな感じです。

パラメータ検証属性を作る

これだけでは面白くないので、少し実用になるかもしれない属性を書いてみましょう。

ところで前述のあえとす氏の記事の後編では、C#でPowerShellのパラメータ検証属性を作る方法について解説されています。ただ、C#でPowerShellの実行空間にアクセスして、操作を行ったり情報を取得したりするのは、少し知識が必要な部分かと思います。

そこで、PowerShellで使う属性なんだからPowerShellで書いたら楽になるんじゃない?という発想で、パラメータ検証属性をクラス構文で書いてみました。

このサンプルは、パラメータ(または変数)値の型が、指定の型セットに含まれているかどうかを検証するものです。複数型を取るようにするためにパラメータの型を[psobject]にすることが良くありますが、この属性を利用して、指定可能な型を絞り込めるようになります。

using namespace System.Management.Automation

[AttributeUsage(([AttributeTargets]::Property) -bor ([AttributeTargets]::Field))]
class ValidateTypeSetAttribute : ValidateEnumeratedArgumentsAttribute
{
    [Type[]]$Types

    ValidateTypeSetAttribute($Types)
    {
        $this.Types = $Types
    }

    [void]ValidateElement([object]$element)
    {
        if(!($element.GetType() -in $this.Types))
        {
            throw New-Object ValidationMetadataException `
                "$($element.GetType().FullName) は許容されない型です。値は $($this.Types) のいずれかの型である必要があります。"
        }
    }
}

使い方は以下の通り。パラメータ検証属性の場合は、定義と同一スクリプト内で呼び出しても、"Attribute"を省略しても問題ないようです。

function test
{
    param(
        [ValidateTypeSet(,([int],[string],[double]))]
        [psobject]
        $obj
    )
}

test "a" #OK
test (Get-Process)[0] #NG

class構文のコンストラクタで可変長引数を定義する方法が分からなかったので、呼び出しがちょっと不格好ですが、そこはご容赦を。

2014/04/30

はじめに

IE6〜11まで、要するに現行のIEすべてを対象とするやばげなゼロデイ脆弱性がみつかり、パッチ公開までIE以外のブラウザを使いましょうという通達が出たり出なかったりしている昨今のようです。

その文脈で、IE以外のWebブラウザをダウンロードするのにIEを使うしかない!もう死ぬしか…みたいな(本気なのか冗談なのか判断が付きかねる)反応を散見します。

実際のところはWebブラウザを探してダウンロードする位はIE使えばいいと思いますが、IE使わないでいかにWebブラウザをダウンロードするか、を考えるのが、Twitter等で一種の大喜利のようになっています。

ftp.exeを使う、wgetなどのコマンドラインツールを使う、ペイントのファイルダイアログを使う(?)等々いろいろな案がありますが、皆なぜPowerShellを使わないんだ。ということで、書きます。

なお、WebブラウザのダウンロードURLはご自分でお調べください。こちらの方の記事が参考になるかと。

Invoke-WebRequestコマンドレットを使う

一番普通のやり方としては、Invoke-WebRequestコマンドレットを使う方法ですね。

Invoke-WebRequest https://アドレス/setup.exe -OutFile setup.exe

これを実行するとファイルがダウンロードされて、カレントディレクトリにsetup.exeというファイルが生成されます。フルパス指定でももちろんOKです。

なお、Invoke-WebRequestコマンドレットはHTMLのDOMパース時のみIEコンポーネントを用いるのですが、-OutFileパラメータ使用時はパースしないのでおそらくIEとは無関係で実行できると思います。(注:IEコンポーネントによるDOMパースを抑制するには、-UseBasicParsingパラメータを付加します)

Invoke-WebRequestコマンドレットはPowerShell 3.0から追加されたコマンドレットなので、3.0が同梱されているWindows 8、4.0が同梱されているWindows 8.1では特に何もせずに利用可能です。

ちなみにInvoke-WebRequestコマンドレットはデフォルトエイリアスとしてiwrが定義されているので、iwrでも呼び出せます。PowerShell 4.0ではそれに加えてwget、curlもエイリアス定義されていたりします。(このエイリアス定義は賛否両論ですけどね)

Windows 7の場合はPowerShellのデフォルトのバージョンは2.0なので、3.0以上を追加で入れる必要があります。Vistaは2.0までしか入らないので残念でした。

Start-BitsTransferコマンドレットを使う

Windows 7以上であればBITS(バックグラウンド インテリジェント転送サービス)の機能がPowerShellコマンドレットから利用できます。BITSはその名の通り、ネットワーク帯域の空き部分を有効活用してファイルを転送するかしこいサービスで、Windows Update等で用いられています。ファイル転送のプロトコルとしてはSMBとHTTP(S)をサポートしてるので、Webサイトからファイルをダウンロードするという用途で使うことができます。

ダウンロードを開始するには、Start-BitsTransferコマンドレットを使います。

Import-Module BitsTransfer
Start-BitsTransfer https://アドレス/setup.exe setup.exe

Windows 7標準のPowerShell 2.0だと、Cmdlet Auto Discoveryの機能が働かないため、上記のようにImport-Moduleコマンドレットによる明示的なモジュールロードが必要ですが、PowerShell 3.0以降では不要です。

BitsTransferモジュールには他にもコマンドレットがあり、非同期転送等できたりするので興味のある方はヘルプをみてください。こちらの記事も参考になるかと:PowerShell: ◆Bits転送2

WebClientオブジェクトを使う

今回の大喜利(?)でPowerShellを用いてファイルダウンロードする方法というのもいくつか見かけたのですが、WebClientを使う方法が殆んどだったように思います。まあ今回の記事を書いた動機としては、今はもうWebClient使わなくても標準のコマンドレットでできるよ!ということなんで敢えてここでは書きません。次のツイートを参照して下さい:Twitter / tanakh: IEを使わずにFirefoxをダウンロードする方法、Powe ...

おまけ:chocolateyを使う

あまりPowerShellとは関係ないのですが、そもそもWindowsにはapt-getみたいなパッケージ管理システムはないんかい、という意見をみかけたので紹介します。ChocolateyというNuGetベースのWindows用アプリケーションのパッケージ管理ツールとリポジトリです。

このツール自体のインストールはコマンドプロンプトに、サイトトップに書いてあるコマンドをコピーペーストして実行するだけです。(このコマンド内でPowerShellを呼び出しているので関係あるっちゃある…)

あとはコマンドプロンプトで cinst GoogleChrome とかしてやるとダウンロードとインストールをしてくれると思います。

ところでchocolateyは現在のところコマンドプロンプトベースであり外部ツールですが、次期バージョンのPowerShell 5.0ではOneGetと呼ばれるパッケージ管理システムが追加され、コマンドレットでchocolateyをはじめとする様々なリポジトリからファイルを取得してインストールできるようになる予定なのでご期待ください。

2012/12/14

本記事はPowerShell Advent Calendar 2012の14日目の記事になります。

前回(アドベントカレンダー1日目)は「PowerShellらしい関数の書き方」と題して、パイプライン内でうまく他のコマンドと連携させるための関数をどう書けばいいのか、ということについて書きました。前回の関数の例では入力型と出力型がstringだったのですが、実際は自分で定義した型を入力、出力値に取るように書くのが普通かと思います。今回は、それをするためにどうやって型を定義するのか、そしてその型を関数にどう指定するのか、という話をします。

PowerShellにはクラス定義構文がない

そもそもの話になるんですが、型を定義する、つまりはクラスを記述するためのPowerShellのステートメントやコマンドレットが無いため、PowerShell単独ではできません。なので無理です以上おしまい。…というわけにはいかないので、実際はどうするのがいいのかという話をしていきます。

方法としては大きく分けて二つあると思います。

1.C#など他の.NET言語を用いてクラスを記述する

2.ユーザー定義オブジェクトを作成する

今回は1の方法を説明します。

C#を用いてクラスを記述する

つまりはPowerShellでクラスを定義できないなら、C#を使えばいいじゃない。ということです。幸いPowerShell 2.0からはAdd-Typeというコマンドレットを用いると、C#やVBなど.NET言語のソースをその場でコンパイルしてアセンブリとして現在のセッションに読み込むことが可能です。

たとえば、論理ドライブを表すDriveというクラスを定義してみます。

Add-Type -TypeDefinition @"
    namespace Winscript
    {
        public enum DriveType
        {
            Unknown, NoRootDirectory, RemovableDisk, LocalDisk, NetworkDrive, CompactDisc, RAMDisk
        }

        public class Drive
        {
            public string Name {get;set;}
            public string VolumeName {get;set;}
            public DriveType Type {get;set;}
            public long Size  {get;set;}
            public long FreeSpace  {get;set;}
            public long UsedSpace  {get;set;}
            public string RootPath {get;set;}
        }
    }
"@ -Language CSharpVersion3

このようにC#のコードを文字列として-TypeDefinitionパラメータに与えると、コンパイルされて指定のクラス(ここではWinscript.Drive)がロードされます。

ここで-Language CSharpVersion3というパラメータは指定コードをC# 3.0としてコンパイルすることを指定するため、今回使用している自動実装プロパティなどC# 3.0の構文が利用できます。なおこのパラメータはPowerShell 3.0では不要です。ただし明示しておくとPowerShell 2.0でも正しく動作します。というのも-Languageパラメータ省略時はPowerShell 2.0ではC# 2.0でコンパイルされるのですが、PowerShell 3.0ではC# 3.0でコンパイルするためです(逆にPSv3でC#2.0でコンパイルするには”CSharpVersion2”という新しく追加されたパラメータ値を指定します)

なお、ここでは-TypeDefinitionパラメータを用いてクラス全体を記述しましたが、この例のように列挙体も定義してそれをプロパティの型にするなどせず、すべて基本型のプロパティで完結するのならば、-MemberDefinitionパラメータを使ってメンバ定義だけを行う方が記述が短くなります。以下はWinscript.Manというクラスを定義する例です。

Add-Type -Namespace Winscript -Name Man -MemberDefinition @"
    public int Age {get;set;}
    public string Name {get;set;}
"@ -Language CSharpVersion3

例のようにC#のコード内には特にロジックを記述せず、単にデータの入れ物となるクラスにとどめておくのが良いかと思います。別にロジックを書いてもいいのですが、ISEで記述する限りはC#の編集に関してはただのテキストエディタレベルの恩恵しか受けないですし、それなら最初からVisual Studio使ってC#で全部コマンドレットとして書けばいいのに、ともなりかねないので。PowerShellでは実現困難な処理などがあればそれをメソッドとして書く程度ならいいかもしれません。ただしメソッドを記述してもそれをユーザーに直接使わせるというよりも、関数でラップして使わせる形が望ましいでしょう。

さて、次はこのクラスのオブジェクトを扱う関数を記述していきます。

定義した型のオブジェクトを扱う関数の記述

ここでは3つの関数を定義しています。Get-Drive関数はシステムに含まれるすべての論理ドライブを取得、Show-Drive関数は指定のDriveオブジェクトをエクスプローラで開く、Set-Drive関数は指定のDriveオブジェクトのボリューム名(VolumeNameプロパティ)を変更するものです。

ちなみに関数の動詞部分(ここではGet, Show, Set)は、Get-Verb関数で取得できるリスト以外のものは基本的に使わないようにします。モジュールに組み込んだ場合、インポートのたびに警告が出てしまうので。

関数の基本については前回に書いているので、今回のコードはそれを踏まえて読んでみてください。

function Get-Drive
{
    [OutputType([Winscript.Drive])]
    param(
        [string[]]$Name,
        [Winscript.DriveType]$Type
    )

    Get-WmiObject -Class Win32_LogicalDisk | ForEach-Object {
        if($null -ne $Name -and $Name -notcontains $_.Name)
        {
        }
        elseif($Type -ne $null -and $_.DriveType -ne $Type)
        {   
        }
        else
        {
            New-Object Winscript.Drive -Property @{
                Name = $_.Name
                VolumeName = $_.VolumeName
                Type = [enum]::Parse([Winscript.DriveType],$_.DriveType)
                RootPath = if($_.ProviderName -ne $null){$_.ProviderName}else{$_.Name + "\"}
                Size = $_.Size
                FreeSpace = $_.FreeSpace
                UsedSpace = $_.Size - $_.FreeSpace
            }
        }
    }
}

(↑10:33 foreachステートメントではなくForEach-Objectコマンドレットを使うように修正。Get-*な関数のようにパイプラインの先頭で実行する関数でも、内部でPowerShellのコマンドレットや関数の出力を利用する場合は、配列化してforeachするよりも、ForEach-Objectで出力を逐次処理した方が良いですね。内部関数の出力がすべて完了してから一気に出力するのではなく、内部関数が1個オブジェクトを出力するたびに出力するようにできるので。)

function Show-Drive
{
    [OutputType([Winscript.Drive])]
    param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [Winscript.Drive[]]
        $Drive,
        
        [switch]
        $PassThru
    )

    process
    {
        foreach($d in $Drive)
        {
            Start-Process $d.Name
            if($PassThru)
            {
                $d
            }
        }
    }
}
function Set-Drive
{
      param(
        [Parameter(ValueFromPipeline=$true,Mandatory=$true)]
        [Winscript.Drive]
        $Drive,
        
        [Parameter(Mandatory=$true)] 
        [string]
        $VolumeName
    )

    process
    {
        Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='$($Drive.Name)'" |
            Set-WmiInstance -Arguments @{VolumeName=$VolumeName} | Out-Null
    }
}

細かい説明は省きますが、前回説明した関数の基本フォーマットに、自分で定義した型を適用してロジックを書くとこうなる、という参考例としてとらえてください。

一つだけ前回に説明し忘れてたことがあります。それは[OutputType]属性です。これは文字通り、関数の出力型を指定するものです。この属性を指定しておくと何が嬉しいかというと、関数の出力を変数に代入したりWhere-Objectコマンドレットでフィルタをかけるコードを記述する際、関数の実行「前」にもプロパティ名をちゃんとタブ補完してくれるようになります。残念ながらこの静的解析機能はPowerShell 3.0からのものなので2.0だとできませんが、OutputType属性自体は2.0でも定義可能なので、定義しておくことを推奨します。

さて、型の定義と関数の定義をしたので実際に関数を実行してみます。

PS> Get-Drive # 全ドライブ取得

Name       : C:
VolumeName :
Type       : LocalDisk
Size       : 119926681600
FreeSpace  : 12262494208
UsedSpace  : 107664187392
RootPath   : C:\

Name       : D:
VolumeName :
Type       : LocalDisk
Size       : 500086886400
FreeSpace  : 198589583360
UsedSpace  : 301497303040
RootPath   : D:\

Name       : Q:
VolumeName :
Type       : CompactDisc
Size       : 0
FreeSpace  : 0
UsedSpace  : 0
RootPath   : Q:\

Name       : V:
VolumeName : 
Type       : NetworkDrive
Size       : 1500299390976
FreeSpace  : 571001868288
UsedSpace  : 929297522688
RootPath   : \\server\D

PS> Get-Drive | where {$_.Size -gt 1TB} # Where-Objectでフィルタ

Name       : V:
VolumeName : 
Type       : NetworkDrive
Size       : 1500299390976
FreeSpace  : 571001868288
UsedSpace  : 929297522688
RootPath   : \\server\D

PS> Get-Drive -Type NetworkDrive | Show-Drive -PassThru | ConvertTo-Csv #ネットワークドライブのみエクスプローラーで開く。取得結果はCSVとして出力。
#TYPE Winscript.Drive
"Name","VolumeName","Type","Size","FreeSpace","UsedSpace","RootPath"
"V:","","NetworkDrive","1500299390976","571001868288","929297522688","\\server\D"
PS> Get-Drive -Name D: | Set-Drive -VolumeName 新しいドライブ # D:ドライブのボリューム名を指定。(管理者権限で)

関数をきちんとPowerShellの流儀に従って記述したおかげで、このようにPowerShellの他の標準コマンドレットと同様の呼び出し方ができ、自作関数やそれ以外のコマンド同士をうまくパイプラインで繋げて実行することができています。

さて、おそらく一つ気になる点があるとすれば、ドライブの容量表示が見づらいということでしょう。容量であればGBとかの単位で表示してほしいですし、大きい数字は,で桁を区切ってほしいですよね。じゃあそういう値を文字列で返すプロパティを定義してやる必要があるというかと言えばそんなことはなく、PowerShellには型に応じた表示フォーマットを指定する方法が用意されています。次回はそのあたりを解説しようと思います。

また、C#とかめんどくさいしもうちょっと楽な方法はないのか?ということで、最初の方でちょっと触れた、ユーザー定義オブジェクトを利用する方法も、余裕があれば次回に。

さて、PSアドベントカレンダー、明日はsunnyoneさんです。よろしくお願いします!

2010/06/25

今月のWindows UpdateでVista/XP/Server 2003/Server 2008用のWindows PowerShell 2.0が提供開始になりました。Windows Server 2008の場合では次のような表示になります。

Windows Server 2008 用の Windows PowerShell 2.0 および WinRM 2.0 (KB968930)

ダウンロード サイズ: 32.4 MB

この更新プログラムを有効にするには、コンピューターを再起動する必要があります。

更新プログラムの種類: オプション

Windows 管理フレームワーク コア パッケージには、Windows PowerShell 2.0 および Windows リモート管理 (WinRM) 2.0 が含まれています。Windows 管理フレームワークの詳細については、http://support.microsoft.com/kb/968929 を参照してください。

詳細情報:
http://go.microsoft.com/fwlink/?LinkID=165613

 

公式ブログの記事:Windows PowerShell 2.0 on Windows Update - Windows PowerShell Blog - Site Home - MSDN Blogs

このアップデートは強制ではなくオプションです。このアップデートを適用すると、Windows 管理フレームワーク (Windows PowerShell 2. 0、WinRM 2. 0、および BITS 4. 0) をインストーラーを使用して適用するのと同様にPowerShell 2.0を導入することができます。なお、このインストーラーではPowerShell 1.0があらかじめシステムにインストールされている場合は前もってアンインストールする必要がありましたが、Windows Updateの場合はアンインストールの必要はなく、自動的に1.0が上書きされ2.0になります。なお.NET Framework 2.0 sp1以上がインストールされていない場合、または、正式版でないPowerShellがシステムにインストールされている場合、このアップデートは候補に現れません。

このアップデートはアンインストールすることができます。その場合はコントロールパネルの「プログラムと機能」などで「更新プログラム」を表示し、「Windows Management Framework Core」を選択します。なおこのアップデートをする前にv1.0をインストールしていた場合は、当該アップデートをアンインストールすることでPowerShellのバージョンがv2.0からv1.0に戻ります。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/06/25/190599.aspx

2010/02/22

PowerShell 2.0では標準コマンドレットの数が1.0の129個から236個へと、107個増えています。また、既存のコマンドレットの一部にパラメータが増えています。そこで、2.0で新しく加わったコマンドレットと、新しく追加されたパラメータを列挙するスクリプトを作ってみました。

まず、1.0がインストールされている環境で、コマンドレットのリストをXMLに書き出します。次のコマンドを実行してください。

 get-command -type cmdlet|export-clixml cmdlets1.xml -depth 3

同様に、2.0の環境でも実行してください。

 get-command -type cmdlet|export-clixml cmdlets2.xml -depth 3

出来上がった二つのxmlファイルをカレントディレクトリに置いて、次のスクリプトを実行すると、新しく追加されたコマンドレットとパラメータが列挙されます。(必要なら適宜リダイレクトするなどしてファイルに落とし込んでください)

function Get-CmdletHash
{
    param([string]$path)
    $cmdlets = Import-Clixml $path
    $cmdletsHash = @{}
    $cmdlets|
        %{
            $parameters = @()
            $_.ParameterSets|
                %{
                    $_.Parameters|
                        %{
                            $parameters += $_.Name
                        }
                }
            $parameters = $parameters|Sort-Object|Get-Unique|?{"WarningAction","WarningVariable" -notcontains $_}
            $cmdletsHash.Add($_.Name,$parameters)
        }
    return $cmdletsHash
}

$ver1 = Get-CmdletHash cmdlets1.xml
$ver2 = Get-CmdletHash cmdlets2.xml

$ver2.Keys|
    %{
        if($ver1.ContainsKey($_))
        {
            $result = Compare-Object $ver1[$_] $ver2[$_]
            if($result)
            {
                "[Update] $_"  
                $result | Format-Table
            }
        }
        else
        {
            "[New] $_  `r`n" 
        }
    }

出力結果を置いておきます。こちら

このスクリプトではGet-CommandコマンドレットがSystem.Management.Automation.CmdletInfoというコマンドレットの情報を格納したオブジェクトを返すことを利用し、Export-Clixmlコマンドレットでシリアライズ化してXMLファイルに落とし込むことにより異なるバージョンのPowerShell同士を比較しています。ここで-depthパラメータを3にしているのは、パラメータ情報を格納するParametersプロパティがルートから3階層下にあるためです。(デフォルトは2階層下までを出力。PS2.0ではCmdletInfoにParametersプロパティというのがあって2階層でたどれるのですが1.0にこのプロパティはないのでParameterSetsプロパティからたどる必要があります)

また、WarningActionとWarningVariableという共通パラメータが増えているので、これらはすべてのコマンドレットに共通して追加されているパラメータなので除外しておきます。

あとはパラメータをコマンドレットごとに配列化して、Compare-Objectで比較しています。新しく追加されたコマンドレットは[New]のマークをつけ、既存のコマンドレットでパラメータに変化があるものは[Update]をつけて追加されたパラメータを列挙するようにしています。

かなりたくさんのコマンドレットでパラメータが増えたことが分かりますね。また、Get-CommandコマンドレットのPSSnapinパラメータが廃止され、Moduleパラメータに変更されたことなどが分かったりします。

1.0ユーザーだった方にお勧めのスクリプトです。

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/02/22/186334.aspx

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

2010/01/01

あけましておめでとうございます!

ついに10年代の到来です。今まで以上にプログラミングもできるライターとして躍進していきたいですね。

読者のみなさま、わんくま同盟のみなさま、編集者のみなさま、お客様、どうぞよろしくお願いいたします。

2.0になって進化したPowerShellもよろしく!

元記事:http://blogs.wankuma.com/mutaguchi/archive/2010/01/01/184483.aspx

2009/10/22

10/31 Admintech.jp大阪勉強会でPowerShell 2.0の話をします。内容的にはサーバー管理者向けに2.0の新機能を中心にPS2を概説するという感じです。わんくまで7/4しゃべった内容のRTM版対応という感じです。
http://itpro.admintech.jp/wiki/wiki.cgi?page=%C2%E817%B2%F3+Admintech%2Ejp%CA%D9%B6%AF%B2%F1

お時間があるかたでご興味のあるかた、ぜひどうぞ。
Active Directoryの大家、小鮒さんの講演が聴けます!(PowerShell 2.0のActive Directoryモジュールの話もしてくださるみたいです)

この日はWindows 7のイベントが大阪であるそうで、ちょっとかぶってしまいましたが、どちらも魅力的なイベントだと思いますのでどちらかぜひぜひ。

あと、その前の週というか今週の土曜日ですが、わんくま大阪勉強会もありますのでみなさんふるってご参加ください。グループディスカッションがあって面白そうですよ。こちらは私は今回スタッフ参加です。http://wankuma.com/seminar/20091024osaka32/Default.aspx

元記事:http://blogs.wankuma.com/mutaguchi/archive/2009/10/22/182321.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-2018 Daisuke Mutaguchi All rights reserved
mailto: mutaguchi at roy.hi-ho.ne.jp
プライバシーポリシー

Twitter

Books