カスタムプロバイダーを作ってみよう ラスト

by 山口 22. 10月 2008 18:51

前回の続きです。

今回はSet-Contentコマンドレットへの対応からはじめます。

 

Set-Contentコマンドレットへの対応

Set-Contentコマンドレットに対応するには、IContentCmdletProviderインターフェースの以下のメソッドを実装します。

  • void ClearContent
  • IContentWriter GetContentWriter(string path)
  • object GetContentWriterDynamicParameters(string path)

Set-Contentコマンドレットはファイルの内容を置き換える為、GetContentWriterメソッドが呼び出される前にClearContentメソッドが呼び出されて、コンテントの内容をクリアするようになっています。 なのでClearContentメソッドを実装する必要があります。

まずはIContentWriterインターフェースを実装したクラスを定義します。

IContentWriterインターフェースには以下のメソッドが定義されています。

  • void Close()
  • void Seek(long offset, SeekOrigin origin) 
  • IList Write(IList content)

まずは、このインターフェースを実装したクラスを定義します。

using System;
using System.IO;
using System.Text;
using System.Management.Automation.Provider;
using System.Collections;

namespace PSHoge {
     public class HogeFileContentWriter : IContentWriter {
         private StreamWriter sw;

         public HogeFileContentWriter(string path, Encoding encoding) {
             sw = new StreamWriter(path, true, encoding);
         }
         ~HogeFileContentWriter() {
             Dispose();
         }

         public void Close() {
             if(sw != null) {
                 sw.Close();
                 sw = null;
             }
         }
         public void Seek(long offset, SeekOriginorigin) {
             throw new NotImplementedException();
         }
         public IList Write(IList content) {
             foreach(var c in content) {
                 sw.WriteLine(c.ToString());
             }
             return null;
         }

         public void Dispose() {
             Close();

             GC.SuppressFinalize(this);
         }
     }
}

コンストラクタでファイル名とエンコードを指定し、ファイルを追記モードで開いています。

Writeメソッドではリストで渡された要素の数だけファイルに内容を出力しています。 あとはお馴染みですね。

今回は始めから動的パラメータも定義しておきます。

[Serializable]
[DebuggerStepThrough]
public class SetContentParameters : FileSystemContentWriterDynamicParameters {
}

FileSystemContentWriterDynamicParametersクラスから派生しておくと楽ができます。

ClearContent、GetContentWriterメソッドを実装します。

public void ClearContent(string path) {
     if(File.Exists(path)) File.Delete(path);
}

public IContentWriter GetContentWriter(string path) {
     var scParams = base.DynamicParameters as SetContentParameters;
     var encoding = scParams != null ? scParams.EncodingType : Encoding.UTF8;

     return new HogeFileContentWriter(path, encoding);
}

public object GetContentWriterDynamicParameters(string path) {
     return new SetContentParameters();
}

ClearContentメソッドではファイルを削除しています。

GetContentWriterメソッドではHogeFileContentWriterクラスをインスタンス化して返しておきます。

デバッグ実行して、以下のように動作を確認できます。

PS Hoge:\> Set-Content 400.txt hoge

これでSet-Contentコマンドレットに対応できました。

 

まとめ

ここまでカスタムプロバイダーの作り方について説明してきましたが、いかかでしょうか?だいたいイメージは掴めたのではないでしょうか。

カスタムプロバイダーを作るための手順をまとめておきます。

  1. NavigationCmdletProviderクラスから派生して、プロバイダークラスを作る。
    1. IsValidPathメソッドをオーバーライドする。
    2. Set-LocationできるようにItemExistsIsItemContainerメソッドをオーバーライドする。
    3. 各コマンドレットに対応したメソッドをオーバーライドする。
      Get-ChildItem
      コマンドレットならGetChildItemsメソッド
      New-ItemコマンドレットならNewItemメソッドという具合
  2. PSSnapInクラスから派生してインストーラクラスを作る。
  3. InstallUtil.exeを使ってアセンブリをPowerShellにインストールする。
  4. Add-PSSnapinコマンドレットを使ってスナップインを追加する。
  5. New-PSDriveコマンドレットでカスタムプロバイダーのPSDriveを追加する。

といった感じです。

今回説明した以外にもNew-PSDriveに対応するNewDriveメソッドをオーバーライドして、PSDriveを追加する時の条件を変更できたりもします。

他にもパスの区切り記号の変更などもできたりもします(デフォルトでは円記号)。

カスタムプロバイダーを作る上で最も難しい事はどのようなデータソースをプロバイダーで抽象化するかということでしょう。

例えば、SQL Serverであったり、FTPサイトであったり、WebDAV、Amazon S3など色々と考える事ができます。結局のところはアイデア勝負ということです。

何か抽象化できそうなデータソースがあれば、試しにカスタムプロバイダーを作ってみることをお勧めします。

それでは楽しいPowerShellライフを!!

 

ソース


目次

最新のブログ

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

カスタムプロバイダーを作ってみよう その4

by 山口 20. 10月 2008 22:15

前回の続きです。

今回はタブ補完から実装します。

 

タブ補完への対応

タブ補完に対応させるには、以下のメソッドをオーバーライドします。

  • void GetChildNames(string path, ReturnContainers returnContainers)

実装する前にタブ補完とはどのような機能なのかということを説明しておきます。

PowerShellでcdやremなどのコマンドを実行する時に、その引数としてファイル名などのパスを渡す必要がありますが、その時パスを全て入力するのではなく、例えば「100.txt 」を入力する時に「1」だけ入力してTabキーを押すと自動的に補完してくれます。再度Tabキーを押すと次の候補を表示してくれる、という機能がタブ補完です。

PS c:\> cd W

Tabキーを押すと、

PS c:\> cd WINDOWS

補完される。

では、さっそく実装してみましょう。

protected override void GetChildNames(string path, ReturnContainers returnContainers) {
     foreach(var f in Directory.GetFiles(path)) {
         base.WriteItemObject(Path.GetFileName(f), f, false);
     }
}

やる事は単純です。渡されたパスに存在するファイルの一覧を列挙して、ファイル名だけをパイプラインに出力します。これだけです。

こうすることで、タブ補完の候補がPowerShellに渡される事になります。後はPowerShell側が勝手にやってくれます。

デバッグ実行して、動作を試してみて下さい。何故かこのメソッドは二回呼び出されますが気にしないでおきましょう。

 

まだ実装していない機能で、

  • Set-Item
  • Get-Item
  • Rename-Item 
  • Copy-Item

などがありますが、これらはほとんど今までと同じ方法で実装できるのでこれ以上の説明はしません。

なので、他とは少し変わった機能であるGet-ContentSet-Contentコマンドレットを実装してみます。

 

Get-Contentコマンドレットへの対応

Get-Contentコマンドレットに対応するにはプロバイダーはIContentCmdletProviderインターフェースを実装する必要があります。

IContentCmdletProviderインターフェースには以下のメソッドが定義されています。

  • void ClearContent(string path) 
  • object ClearContentDynamicParameters(string path)
  • IContentReader GetContentReader(string path)
  • object GetContentReaderDynamicParameters(string path)
  • IContentWriter GetContentWriter(string path)
  • object GetContentWriterDynamicParameters(string path)

動的パラメータのありなしをペアとして三つのメソッドがあります。

この中でGet-Contentの為に必要なのはGetContentReaderメソッドとそれの動的パラメータ版です。

GetContentReaderメソッドはIContentReaderインターフェースを実装したオブジェクトを返す必要があります。

IContentReaderインターフェースには以下のメソッドが定義されています。

  • void Close() 
  • IList Read(long readCount)
  • void Seek(long offsert, SeekOrigin origin)
  • void Dispose()

なんとなくストリームっぽい感じですね。それぞれの役割はだいたいわかるでしょう。

とりあえずこのインターフェースを実装したクラスを作ってみます。

public class HogeFileContentReader : IContentReader {

     private StreamReader sr;
     
     public HogeFileContentReader(string path) {
         sr = File.OpenText(path);
     }
     ~HogeFileContentReader() {
         Dispose();
     }

     public void Close() {
         if(sr != null) {
             sr.Close();
             sr = null;
         }
     }
     public IList Read(long readCount) {
         var lines = new List<string>();

         for(var i = 0; i < readCount; i++) {
             var line = sr.ReadLine();

             if(line == null) break;

             lines.Add(line);
         }
         return lines;
     }
     public void Seek(long offset, SeekOrigin origin) {
         throw new NotImplementedException();
     }

     public void Dispose() {
         Close();

         GC.SuppressFinalize(this);
     }
}

コンストラクタで引数にパスを指定してファイルを読み込み、ReadメソッドでreadCountの分だけファイルを読み込んで、その結果をリストで返しています。後はお馴染みの処理ですね。

あとはこのクラスをGetContentReaderメソッドでインスタンス化して返すだけです。

public IContentReader GetContentReader(string path) {
     return new HogeFileContentReader(path);
}

public object GetContentReaderDynamicParameters(string path) {
     return null;
}

動的パラメータはいらないのでnullを返しておきます。

これでデバッグ実行して、以下のコマンドを実行してみて下さい。

PS Hoge:\> Get-Content 300.txt

これでファイルの中身が出力されるはずです。

ちなみにこのままではUTF8のファイルしか正常に表示できないので、動的パラメータでエンコードを指定できるようにしておきましょう。

まずはパラメータクラスを定義します。

[Serializable]
[DebuggerStepThrough]
public class GetContentParameters : FileSystemContentReaderDynamicParameters {
}

Get-Contentの動的パラメータクラスはFileSystemContentReaderDynamicParametersクラスから派生すると便利です。Encodingプロパティも既に定義されています。

このクラスのインスタンスを返すようにGetContentReaderDynamicParametersメソッドを変更しておきます。

public object GetContentReaderDynamicParameters(string path) {
     return new GetContentParameters();
}

HogeFileContentReaderのコンストラクタもエンコードを指定できるように変更しておきます。

public HogeFileContentReader(string path, Encoding encoding) {
     sr = new StreamReader(path, encoding);
}

GetContentReaderメソッドも動的パラメータを取得するように変更しておきます。

public IContentReader GetContentReader(string path) {
     var gcParams = base.DynamicParameters as GetContentParameters;
     var encoding = gcParams != null ? gcParams.EncodingType : Encoding.UTF8;

     return new HogeFileContentReader(path, encoding);
}

これでGet-Contentコマンドレットでエンコードを指定できるようになりました。

PS Hoge:\> Get-Content -Encoding UTF8 300.txt 

Get-Contentの説明が長くなってしまったので、Set-Contentは次回にします。

 

目次

最新のブログ

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Hey, Scripting Guy! で

by HIRO 20. 10月 2008 22:09

Windows PowerShell を使用してローカル グループにドメイン ユーザーを追加する方法はありますか」が公開されています。

Active Directory ユーザー アカウントへのオブジェクト参照を作成する方法、実際にユーザーを追加する方法などが説明されています。

興味のある方はぜひどうぞ。

最新のブログ

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

PowerShell

SQLServer 2008 でPowerShellを使おう~DB&テーブル作成編~

by けろ-みお 19. 10月 2008 23:01

 

はじめまして、私、けろ-みお と申します。

元々、管理人のHIROさんとは長い付き合いでして、

techbank.jp / techbank.jp コミュニティ! というところでも一緒にコミュニティ運営しております。

この度、SQL Server 2008 で標準搭載されたPowerShellを実践でも活用して頂きたく思い、初投稿することにしました。

元々、私の専門がASP/ASP.NET、SQLServerなもので、まだまだPowerShellは不慣れですが、どうぞ皆様宜しくお願いします。 

 

◆PowerShellを使ってSQL Server 2008上にDB(データベース)を作成する

###################################################################
# 新しいDBを作成する
###################################################################
$NewDb = New-Object [Microsoft.SqlServer.Management.SMO.Database]
$NewDb.Parent = (Get-Item SQLSERVER:\SQL\[サーバー名]\[SQLServerサービスインスタンス名])
$NewDb.Name = "[新しく作成したいDB名]"
$NewDb.Create() 
 
###################################################################
# []内は環境に応じて変えて下さい。(その際、[]の文字部分も不要)
# [サーバー名]は、localhostなどのSQLServerがインストールされているマシン名かIPアドレス
# [SQLServerサービスインスタンス名]は、「MSSQLSERVER」等の名前
# [新しく作成したいDB名]は、新規作成したいDB名を任意で入れてください。
################################################################### 

 

実行結果:

 

◆PowerShellを使って、SQL Server 上に簡単なテーブルを作成してみよう!

###################################################################################
# 新しいテーブルを作成する
###################################################################################
$table = New-Object Microsoft.SqlServer.Management.Smo.Table
$table.Parent = (Get-Item SQLSERVER:\SQL\[サーバー名]\[SQLServerサービスインスタンス名]\Databases\[DB名])
$table.Name = "NewTableName" 
 
###################################################################################
# 作成するテーブル列を作成する
# (必ずNameプロパティで列名を定義してから、DataTypeプロパティで列の型を定義すること!
# そうしないと
# 「"DataType" の設定中に例外が発生しました: "列 '' のDataType の設定に失敗しました。"」
# とエラーになる)
###################################################################################
 
# int型の列を作成する
$column1 = New-Object Microsoft.SqlServer.Management.Smo.Column
$column1.Parent = $table
$column1.Name = "column1"
$column1.DataType = ([Microsoft.SqlServer.Management.Smo.DataType]::Int) 
 
# 作成した列をテーブルに追加
$table.Columns.Add($column1) 
 
# nvarchar型の列を作成する
$column2 = New-Object Microsoft.SqlServer.Management.Smo.Column
$column2.Parent = $table
$column2.Name = "column2"
$column2.DataType = ([Microsoft.SqlServer.Management.Smo.DataType]::NVarChar(100)) 
 
# 作成した列をテーブルに追加
$table.Columns.Add($column2) 
 
# テーブル作成実行
$table.Create() 

実行結果:

 

◆PowerShellを使って、主キーや項目のNULL制御されているきちんとしたテーブルをSQLServer上に作成してみよう!

##################################################################################
# 主キーやNOT NULL項目ありのテーブルを作成する
##################################################################################
 
$table = New-Object Microsoft.SqlServer.Management.Smo.Table
$table.Parent = (Get-Item SQLSERVER:\SQL\[サーバー名]\[SQLServerサービスインスタンス名]\Databases\[DB名])
$table.Name = "NewTableName2"
 
# まずはテーブルに追加する列を作成する
$column1 = New-Object Microsoft.SqlServer.Management.Smo.Column
$column1.Parent = $table
$column1.Name = "column1"
$column1.DataType = ([Microsoft.SqlServer.Management.Smo.DataType]::Int) 
 
# 列はNULLを許可しないよう設定
$column1.Nullable = ([System.Boolean]::false) 
 
# IDENTITYに対応させる(1から自動採番されるよう列を設定する)
$column1.IdentitySeed = 1
$column1.IdentityIncrement = 1 
 
# 作成した列をテーブルに追加
$table.Columns.Add($column1) 
 
# 主キーを作成する
$index = New-Object Microsoft.SqlServer.Management.Smo.Index
$index.Parent = $table
$index.Name = "PK_NewTable2"
 
# 主キーとして認識させるためにClustered Indexを作成する
$indexedColumn = New-Object Microsoft.SqlServer.Management.Smo.IndexedColumn
$indexedColumn.Parent = $index
$indexedColumn.Name = "column1" 
 
# Clustered Indexを適用する
$index.IndexedColumns.Add($indexedColumn)
$index.IndexKeyType = ([Microsoft.SqlServer.Management.Smo.IndexKeyType]::DriPrimaryKey) 
 
# 作成したClustered Indexをテーブルに追加
$table.Indexes.Add($index) 
 
# テーブルを作成する
$table.Create() 

 

実行結果:

 

◆まとめ

Microsoft.SqlServer.Management.Smoアセンブリ&名前空間の使い方がわかれば、

PowerShellじゃなくても活用はできると思いますが、 バッチ処理でDBやテーブルを作成しなきゃいけないとか、

ちょっとしたツールとしてDBを作らなきゃいけないとかそういった場合、

PowerShellコマンドレットで処理させると、 実務での活用どころも期待できると思っています。

どうぞお役立て頂きたく存じます。

 

◆その他

同様の記事を自身のBlog

http://techbank.jp/Community/blogs/mymio/default.aspx

にもクロスポストしています。予めご了承ください。

 

現在のレート 5.0 (3人)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

PowerShell | その他

カスタムプロバイダーを作ってみよう その3

by 山口 19. 10月 2008 11:44

前回の続きです。

今回はNew-Itemコマンドレットへの対応から始めます。

 

New-Itemコマンドレットへの対応

New-Itemコマンドレットに対応させるには、以下のメソッドをオーバーライドします。

  • void NewItem(string path, string itemTypeName, object newItemValue) 

FileSystemProviderでNew-Itemコマンドレットを使う場合を考えてみましょう。

PS c:\> New-Item 100.txt File -itemType File -value Hello

ファイル名とitemTypeに「File」か「Directory」、valueにファイルの内容を指定します。

これらのパラメータがNewItemメソッドのそれぞれの引数に対応する事は明白ですね。それでは実装してみましょう。

protected override void NewItem(string path, string itemTypeName, object newItemValue) {
     using(var fs = File.Create(path)) {
         base.WriteItemObject(new HogeFile {
             FileName = Path.GetFileName(path),
             Size = fs.Length

         }, path, false);
     }
}

渡されたパスでファイルを作って、その情報をHogeFileオブジェクトとしてパイプラインに出力しています。

デバッグ実行して、動作をテストしてみましょう。

PS Hoge:\> New-Item 400.txt
ファイル名                                        ファイルサイズ
----------                                        --------------
400.txt                                           0

itemTypeNamenewItemValueへの対応はやめておきます。その代わり新しいパラメータを定義してみます。

例えば、以下のように-BufferSizeというパラメータで数値を指定すると、

PS Hoge:\>New-Item 400.txt -BufferSize 128
ファイル名                                        ファイルサイズ
----------                                        --------------
400.txt                                           128

そのサイズでファイルが作成されるというものです。

こういった新しいパラメータを定義するためのDynamicParametersという仕組みがプロバイダーには用意されています。

例えばNew-Itemコマンドレットに新しいパラメータを定義するには、NewItemDynamicParametersというメソッドをオーバーライドします。

このメソッドのシグネチャを見てみると、

  • object NewItemDynamicParameters(string path, string itemTypeName, object newItemValue) 

となっていますが、引数はNewItemメソッドと同じで返り値がobject型になっています。このメソッドはNewItemメソッドが呼び出される前に必ず呼び出されるようになっています。

では、このメソッドの返り値として何を返せばいいのでしょうか?

答えは以下のようなパラメータをプロパティとして持つプレーンなクラスを定義して、そのインスタンスを返します。

[Serializable]
[DebuggerStepThrough]
public class NewItemParameters {
     [Parameter]
     public int BufferSize {
         get;
         set;
     }
}

パラメータとして定義するプロパティにはParameter属性でマークをしておきます。

では、このクラスのインスタンスを返すようにNewItemDynamicParametersメソッドを実装しておきます。

protected override object NewItemDynamicParameters(string path, string itemTypeName, object newItemValue) {
     return new NewItemParameters();
}

パラメータを定義したので、NewItemメソッドもこれに対応できるように変更しておきます。

protected override void NewItem(string path, string itemTypeName, object newItemValue) {
     var niParams = base.DynamicParameters as NewItemParameters;
     var bufferSize = niParams != null ? niParams.BufferSize : 0;

     using(var fs = File.Create(path, bufferSize)) {
         fs.Write(new byte[bufferSize], 0, bufferSize);

         base.WriteItemObject(new HogeFile {
             FileName = Path.GetFileName(path),
             Size = fs.Length

         }, path, false);
     }
}

動的パラメータはDynamicParametersプロパティで取得できます。動的パラメータが定義されていれば、BufferSizeを取得して、そのサイズのファイルを作成しています。

これでデバッグ実行すると前述のように動作するはずです。

このようにして既存のコマンドレットに新しいパラメータを追加する仕組みがプロバイダーには用意されています。おもしろいですね!!

ファイル作成ができるようになったので、その逆ファイルの削除もできるようにしておきましょう。

 

Remove-Itemコマンドレットへの対応

Remove-Itemコマンドレットに対応させるには、以下のメソッドをオーバーライドします。

  • void RemoveItem(string path, bool recurse)
  • bool HasChildItems(string path)

RemoveItemメソッドはそのままですが、HasChildItemsメソッドはどういう役割でしょうか?
このメソッドはアイテムを削除する時にそのアイテムに子アイテムがあるかどうかを問い合わせます。このメソッドがfalseを返せばアイテムは削除されますが、trueを返せばRemove-Itemコマンドレットで-forceパラメータが指定された場合のみ削除されます。

これはすごく単純なのでさっさと実装します。

protected override bool HasChildItems(string path) {
     return false;
}

protected override void RemoveItem(string path, bool recurse) {
     File.Delete(path);
}

これだけです。

デバッグ実行して、以下のコマンドを実行するとファイルが削除されるはずです。

PS Hoge:\> Remove-Item 400.txt 

次回はタブ補完を実装します。

 

目次

現在のレート 5.0 (1人)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Calendar

<<  3月 2010  >>
月曜火曜水曜木曜金曜土曜日曜
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010 PowerShell from Japan!!