前回の続きです。

今回は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ライフを!!

 

ソース


目次


Posted in:   Tags:

最新のブログ

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

前回の続きです。

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

 

タブ補完への対応

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

  • 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は次回にします。

 

目次


Posted in:   Tags:

最新のブログ

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
HIRO posted on 10月 20, 2008 22:09

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

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

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


Posted in: PowerShell  Tags:

最新のブログ

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

 

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

元々、管理人の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

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

 


Posted in: PowerShell , その他  Tags:

現在のレート 5.0 (2人)

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

前回の続きです。

今回は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 

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

 

目次


Posted in:   Tags:

現在のレート 5.0 (1人)

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

Posted in: PowerShell , PowerShell入門  Tags:

最新のブログ

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

前回の続きです。

今回はまず、自作のプロバイダーでcd ができるようにする、つまりSet-Locationコマンドレットに対応させます。

その前にこのプロバイダーをデバッグできるようにしておきましょう。

デバッグの設定

Visual Studioのプロジェクトのプロパティを開いて、「デバッグ」タブの「開始動作」を「外部プログラムの開始」に設定します。
そして外部プログラムとして「C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe」(WindowsXPの場合)を指定します。

この時点でPowerShellのデバッグは可能になりますが、自作のプロバイダーが読み込まれないので、読み込まれるように設定します。

「開始オプション」の「コマンドライン引数」に以下のコマンドを指定します。

-NoExit -Command Add-PSSnapin Hoge; [void](New-PSDrive Hoge Hoge c:\temp)

これでデバッグを開始するとプロバイダ-のデバッグが可能になります。

 

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

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

  • bool ItemExists(string path)
  • bool IsItemContainer(string path)

Set-Locationコマンドレットが実行されると、まずプロバイダーのItemExistsメソッドが呼び出されてそのパスが存在するかどうかを問い合わされます。

そして、そのパスが存在すればIsItemContainerメソッドが呼び出されて、そのパスがコンテナかどうか、つまり中に入れるかどうかが問い合わされます。

つまりこの二つのメソッドがtrueを返せば、そのパスには移動が可能ということになります。

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

using System;
using System.Management.Automation.Provider;

namespace PSHoge {
     [CmdletProvider("Hoge", ProviderCapabilities.ShouldProcess)]
     public class HogeProvider : NavigationCmdletProvider {

         protected override bool IsValidPath(string path) {
             return true;
         }

         protected override bool ItemExists(string path) {
             return true;
         }

         protected override bool IsItemContainer(string path) {
             return true;
         }
     }
}

とりあえず、両方のメソッドともtrueを返しておきます。

試しにデバッグ実行をして、Hogeドライブにcdをしてみて下さい。以下のようにHogeドライブに移動できるはずです。

PS > cd Hoge:
PS Hoge:\>

このようにしてSet-Locationコマンドレットに対応します。もちろん実際には問い合わせられたパスが存在するかどうかを調べてその結果を返す必要があります。

重要なのはパスが存在するかどうかを調べる方法がプロバイダーに移譲されている事です。
こうすることでそのパスがファイルシステムであったり、例えばFTPのアドレスであったり、仮想的なリソースであることを許容できます。

では、次はdirコマンド、つまりGet-ChildItemコマンドレットに対応させます。

 

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

Get-ChildItemコマンドレットに対応させるには、上述の二つのメソッドに加えて以下のメソッドをオーバーライドします。

  • void GetChildItems(string path, bool recurse)

Get-ChildItemsコマンドレットが実行されると、Set-Locationコマンドレットの場合と同様にそのパスが存在する事とコンテナであることが問い合わされ、そしてGetChildItemsメソッドでアイテムの一覧が列挙されます。

とりあえず実装してみましょう。

using System;
using System.Management.Automation.Provider;

namespace PSHoge {
     [CmdletProvider("Hoge", ProviderCapabilities.ShouldProcess)]
     public class HogeProvider : NavigationCmdletProvider {

         protected override bool IsValidPath(string path) {
             return true;
         }

         protected override bool ItemExists(string path) {
             return true;
         }

         protected override bool IsItemContainer(string path) {
             return true;
         }

         protected override void GetChildItems(string path, bool recurse) {
         }
     }
}

なにもしていません。

この状態でデバッグ実行して、dirコマンドを実行すると何も表示されませんがエラーも出ません。

では、画面に何か出力してみましょう。画面への出力、正確にはパイプラインへの出力を行うにはWriteItemObjectメソッドを使用します。

FileSystemProviderではFileInfoDirectoryInfoがパイプラインに出力されます。今回は独自のクラスを作ってそのオブジェクトを出力することにします。

using System;
using System.Diagnostics;

namespace PSHoge {
     [Serializable]
     [DebuggerStepThrough]
     public class HogeFile {
         public string FileName {
             get;
             set;
         }
         public long Size {
             get;
             set;
         }
     }
}

ファイル名とファイズサイズを保持する「HogeFile」というクラスを定義しました。

では、カレントディレクトリのファイルを調べて、このオブジェクトを出力する処理を書いてみましょう。

protected override void GetChildItems(string path, bool recurse) {
     foreach(var f in Directory.GetFiles(path)) {
         var fi = new FileInfo(f);
         base.WriteItemObject(new HogeFile {
             FileName = fi.Name,
             Size = fi.Length
         }, f, false);
     }
}

渡されたパスに存在するファイルを列挙して、HogeFileオブジェクトをWriteItemObjectメソッドで出力しています。

デバッグ実行して、dirコマンドを実行すると以下のように表示されます(もちろんc:\tempフォルダにいくつかのファイルがある前提です)。

PS Hoge:\> dir
PSPath        : Hoge\Hoge::c:\temp\100.txt
PSParentPath  : Hoge\Hoge::c:\temp
PSChildName   : 100.txt
PSDrive       : Hoge
PSProvider    : Hoge\Hoge
PSIsContainer : False
FileName      : 100.txt
Size          : 6
PSPath        : Hoge\Hoge::c:\temp\200.txt
PSParentPath  : Hoge\Hoge::c:\temp
PSChildName   : 200.txt
PSDrive       : Hoge
PSProvider    : Hoge\Hoge
PSIsContainer : False
FileName      : 200.txt
Size          : 10
PSPath        : Hoge\Hoge::c:\temp\300.txt
PSParentPath  : Hoge\Hoge::c:\temp
PSChildName   : 300.txt
PSDrive       : Hoge
PSProvider    : Hoge\Hoge
PSIsContainer : False
FileName      : 300.txt
Size          : 14

FileNameプロパティとSizeプロパティがあるのが確認できますが、他にもPSから始まるプロパティが複数あります。これらはNavigationCmdletProviderが自動的に付加するものです。

このままでは見栄えが悪いので、出力されるオブジェクトの書式設定をします。 オブジェクトの書式設定については以前書いた以下の記事を参考にして下さい。

以下がオブジェクトの書式設定ファイルです。

<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
      <SelectionSets>
          <SelectionSet>
              <Name>PSHogeTypes<Name>
              <Types>
                  <TypeName>PSHoge.HogeFile<TypeName>
              <Types>
          <SelectionSet>
      <SelectionSets>

      <ViewDefinitions>
         <View>
              <Name>children<Name>
              <ViewSelectedBy>
                  <SelectionSetName>PSHogeTypes<SelectionSetName>
              <ViewSelectedBy>
              <TableControl>
                  <TableHeaders>
                      <TableColumnHeader>
                         <Label>ファイル名<Label>
                      <TableColumnHeader>
                      <TableColumnHeader>
                         <Label>ファイルサイズ<Label>
                      <TableColumnHeader>
                  <TableHeaders>
                  <TableRowEntries>
                      <TableRowEntry>
                          <TableColumnItems>
                              <TableColumnItem>
                                  <PropertyName>FileName<PropertyName>
                              <TableColumnItem>
                              <TableColumnItem>
                                  <PropertyName>Size<PropertyName>
                              <TableColumnItem>
                          <TableColumnItems>
                      <TableRowEntry>
                  <TableRowEntries>
              <TableControl>
          <View>
      <ViewDefinitions>
<Configuration>

このオブジェクト書式設定ファイルを読み込んで、再度dirコマンドを実行すると以下のように表示されます。

PS Hoge:\>
ファイル名                                        ファイルサイズ
----------                                        --------------
100.txt                                           6
200.txt                                           10
300.txt                                           14

このようにしてGet-ChildItemコマンドレットに対応させます。

次はNew-Itemコマンドレットやタブ補間などに対応させてみます。

 

目次


Posted in:   Tags:

最新のブログ

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
HIRO posted on 10月 14, 2008 21:08

Technet Magazine 10月号が公開されています。

PowerShellに関してはプロファイルについての記事がありましたので、興味がある方はこちらからどうぞ。


Posted in: PowerShell  Tags:

最新のブログ

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

PowerShellの重要な特徴として、ファイルシステムやレジストリ、環境変数など異なるデータソースに対して共通の操作(コマンドレット)が行えるということが挙げられます。

例えば、以下のように「C」ドライブ直下に「New-Item」コマンドレットでファイルを作成できるのと同じ感覚で、

PS c:\> New-Item hoge

「HKLM」ドライブ(HKEY_LOCAL_MACHINE)に移動して、「New-Item」コマンドレットでレジストリキーが作れるといった具合にです。

PS HKLM:\Software > New-Item hoge

これらはプロバイダーという機構によって成り立っています。

ファイルシステムならFileSystemProvider、レジストリならRegistryProvider、環境変数ならEnvironmentProviderと言った具合にそれぞれに対応するプロバイダーが用意されています。

PowerShellには「New-PSDrive」というコマンドレットが用意されています。
このコマンドレットは「PSDrive」というPowerShell上でだけ有効な仮想的なドライブを新規作成するものですが、このコマンドレットのパラメータを見て下さい。

New-PSDrive [-name]  [-psProvider]  [-root]  [-description ] [-scope ] [-credential ]

二つ目の引数に「psProvider」というのがあります。この引数でどのプロバイダーを使ってPSDriveを作成するかを決めるわけです。

例えばFileSystemProviderを使って、「C:\WINDOWS」ディレクトリをルートとする「WIN」ドライブを作る場合、以下のようにします。

PS > New-PSDrive WIN FileSystem C:\WINDOWS
Name       Provider      Root
----       --------      ----
WIN        FileSystem    C:\WINDOWS

このようにして、プロバイダーを指定してPSDriveを作成する事ができます。

これらの事からプロバイダーとは、様々な種類のPSDriveを提供するための機構だということがわかります。

 

開発環境の構築

では、さっそくプロバイダーを作っていきましょう。

開発環境として、

  • PowerShell 1.0
  • Microsoft Visual Studio 2008 Standard Edition (Visual C# 2008 Expressでも可) 

を使用します。

また、プロバイダーの開発には.NET FrameworkとC#に対する基本的な知識が必要になります。

開発用のアセンブリをコピー

プロバイダーの開発には、「System.Management.Automation」というアセンブリが必要になりますが、このアセンブリはGACにのみインストールされているので、ここから取り出す必要があります。

GACにインストールされたアセンブリはWindows Explorerからはコピーができないので、コマンドプロンプトかPowerShellを使ってコピーする必要があります。

「System.Management.Automation.dll」を以下のパスから適当な場所にコピーして下さい。

  • C:\WINDOWS\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll

 

プロジェクトの作成

では、プロバイダーの仕組みを学ぶ為に簡単なプロバイダーを作ってみましょう。

Visual Studio 2008を起動して、言語は「Visual C#」で「クラスライブラリ」プロジェクトを作成します。プロジェクト名は「PSHoge」とします。



まずは先程コピーした「System.Management.Automation.dll」アセンブリを参照設定に追加しておきます。

では、プロバイダーを作ってみましょう。

プロバイダーはCmdletProviderクラスを継承して作ります。
しかし、CmdletProviderには必要最低限の機能しか実装されていないので、ここからプロバイダーを実装するのは結構大変です。

なので、通常はCmdletProviderから派生したNavigationCmdletProviderを使用します。

CmdletProviderNavigationCmdletProviderなどとの継承関係を以下に示します。

プロバイダークラスの継承ツリー

  • CmdletProvider
    • DriveCmdletProvider
      • ItemCmdletProvider
        • ContainerCmdletProvider
          • SessionStateProviderBase
            • AliasProvider
            • EnvironmentProvider
            • FunctionProvider
            • VariableProvider
          • NavigationCmdletProvider
            • FileSystemProvider
            • RegistryProvider

ファイルシステムやレジストリはNavigationCmdletProviderから、環境変数やエイリアスはSessionStateProviderBaseから派生しているのがわかります。

では、NavigationCmdletProviderを継承してプロバイダークラスを作ります。

「HogeProvider.cs」というファイル名でクラスファイルを追加します。

using System;
using System.Management.Automation.Provider;

namespace PSHoge {
     [CmdletProvider("Hoge", ProviderCapabilities.ShouldProcess)]
     public class HogeProvider : NavigationCmdletProvider {

         protected override bool IsValidPath(string path) {
             return true;
         }

     }
}

NavigationCmdletProviderクラスを継承して、IsValidPathメソッドをオーバーライドします。ここではとりあえずtrueを返しておきます。

そして、プロバイダークラスである事を示す為にCmdletProvider属性でマークしています。引数として「Hoge」と「ProviderCapabilities.ShouldProcess」を渡しています。

前者はプロバイダー名で後者はとりあえずこれを指定しておいて下さい。

次にこのプロバイダーをPowerShellにインストールするためのインストーラクラスを作成します。

using System;
using System.Management.Automation;
using System.ComponentModel;

namespace PSHoge {
     [RunInstaller(true)]
     public class HogeSnapIn : PSSnapIn {

         public override string Description {
             get { return "テスト用のプロバイダーです。"; }
         }

         public override string Name {
             get { return "Hoge"; }
         }

         public override string Vendor {
             get { return "coma2n"; }
         }
     }
}

PowerShellのインストーラクラスはPSSnapInクラスから派生させます。DescriptionNameVendorというプロパティをそれぞれ実装する必要があります。

そして、このクラスがインストーラクラスである事を示す為にRunInstaller属性でマークしておきます。

とりあえずここまでで一度PowerShellにこのプロバイダーをインストールしてみましょう。

プロジェクトをビルドして、プロジェクトの出力ディレクトリ(bin\Debug)にコマンドプロンプトで移動して下さい。

以下のコマンドを実行してプロバイダーをインストールします。

PS > InstallUtil.exe PSHoge.dll

正常にインストールが完了したらPowerShellを起動して以下のコマンドを実行します。

PS > Add-PSSnapin Hoge

これでPowerShellにプロバイダーのインストールが完了しました。

あとは前述の「New-PSDrive」コマンドレットを使って、ドライブを新規作成します。

PS > New-PSDrive Hoge Hoge c:\

これで「Hoge」プロバイダーで「Hoge」ドライブを作る事ができました。ルートディレクトリのパスはここでは意味はありませんが、指定する必要があります。

では、試しに「Hoge」ドライブにcdしてみましょう。

Set-Location : プロバイダがこの操作をサポートしていないため、プロバイダの実行が中止されました。
発生場所 行:1 文字:3
+ cd  <<<< hoge:

実はこの段階ではまだcdはできません。では次はこれができるようにしてみましょう。

 

目次


Posted in:   Tags:

現在のレート 5.0 (1人)

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
HIRO posted on 10月 7, 2008 23:15

今日はInvoke-Historyコマンドレットについてまとめてみました。

コマンドレット名
Invoke-History

エイリアス
ihy
r

説明
コマンド実行履歴からコマンドを実行します。Get-History コマンドレットを使用して取得したコマンドを Invoke-History に渡すか、コマンド実行履歴のIDを指定して実行を行います。

下記のように、パラメータなしで Invoke-History コマンドレットを実行すると、最新のコマンドを実行します。

PS > Invoke-History

書式
Invoke-History [[-iD] <string>] [-whatIf] [-confirm] [<CommonParameters>]

パラメータ
-iD <string>
履歴内のコマンドを実行します。履歴のIDを指定するか、コマンドの先頭文字をいくつか指定します。
文字を入力した場合は、最新の履歴から一致するものを探して実行します。
ID番号を指定する場合、Get-History コマンドレットで確認することができます。

–––––––––– -iD パラメータの使用例1 ––––––––––

PS > Invoke-History -id 1

このコマンドを実行すると、Get-History コマンドレットで確認可能な履歴のIDが1のコマンドを実行します。

–––––––––– -iD パラメータの使用例2 ––––––––––

PS > Get–History

  Id CommandLine
  – –––––––––––
   1 Get–Help Invoke–History –Detailed
   2 Get–History
   3 Get–Help Invoke–History –Detailed
   4 Get–Help Invoke–History –Detailed
   5 cls
   6 Get–History
   7 Get–ChildItem
   8 Dir
   9 Get–Content C:\Work\Test.txt

PS > Invoke–History –iD cl

はじめに Get-History コマンドレットで現時点までの履歴を確認しています。
次に、Invoke-History コマンドレットで-iD パラメータに文字 "cl"を指定すると、IDが5のコマンド "cls"がヒットするので、 clsコマンドを実行します。

 


Posted in: PowerShell , コマンドレット  Tags:

最新のブログ

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

Calendar

«  1月 2009  »
月曜火曜水曜木曜金曜土曜日曜
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678