2008年03月16日

PowerShellですごいテクニックを発見した!

最近はPowerShellを常に起動していて、コマンドプロンプトを使わずにPowerShellから色々作業するようにしている(Pythonなどの対話環境もPowerShellから起動している)のですが、そのようななかで個人的にこれはすごいと思えるテクニックを発見しました。

それはたったひとつのコマンド、

PS > powershell

です。「PowerShell内でPowerShellを起動する」。何が嬉しいかというと、スクリプトなりを実行してPowerShellが強制終了した場合に内側のPowerShellが強制終了するだけで外側のPowerShellは生き残るという点です。エラーメッセージも残せます。これで自分でもどうかなと思うような実験的なスクリプトの実行も怖くありません。これはすごい! 画期的!

あー、でもこれ、シェル使いの方には常識だったりするんでしょうか。そうだったら恥ずかしい……。

追記(2008年03月22日)

2chのスレッド「Windows PowerShell (正式版リリース)1.0」の592番目のレスにすでに同様のテクニックが書かれているというコメントをいただきました。けっこうすごい発見だと思ったのですが、やはりというかなんというか、先駆者がおられました。

ただ、こういったコメントをいただくということは「シェル内シェル」はあまり一般的ではないようにも見受けられますね。その点については安心しました。

posted by bakemoji at 23:54| Comment(3) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2008年01月12日

PowerShellにおけるパイプの動作について

久しぶりにPowerShell関連の話題です。2chのスレッド「Windows PowerShell (正式版リリース)1.0」でのパイプの話題を受けて、NyaRuRuさんが「PowerShell で SIGPIPE 連鎖」で以下のようなコードを書いています。

PowerShell 1.0

filter yes {
  while($true){ "y" }
}

filter take([int] $n) { 
  if(0 -gt --$n) { break } 
  $_ 
}

こうやって,

yes | take 10000 | take 1

これがちゃんと 1 行だけ出力して止まる.SIGPIPE 連鎖っぽい.すげえ.

僕も以前似たようなことを試して引っかかったのですが、上記のコードではyesフィルタは1回しか値を返してないと見せかけて、2回返しています。わかりやすいようにyesフィルタを

filter yes {
    while ($true) {
        "say" | Out-Host
        "y"
    }
}

として実行すると、

PS > yes | take 10000 | take 1
say
y
say

と出力されます。フィルタは基本的にprocess節のみの関数なのですが、process節に入るということはすでにパイプから値を取っていることになります。なので、上記のtakeフィルタでは

  1. パイプから値を取る
  2. 終了判定をする
  3. 値を返す

という動作を繰り返すことになり、終了条件を満たしたときにはすでに一個余分に値を取ってしまっているというわけです。

対策として、takeはフィルタではなく関数で以下のように定義します。

function take($n) {
    begin {
        if (0 -ge $n) { break }
    }

    process {
        $_
        if (0 -ge --$n) { break }
    }
}

関数のprocess節内ではパイプからの値はすでに取得してしまっているので終了判定は最後に持っていき、さらにそもそもprocess節に行くべきかどうかを判定するためのbegin節を追加します。実行すると

PS > yes | take 10000 | take 1
say
y

となります。余分なsayがなくなりました。

ここまで書いておいてなんですが、僕も今回書いたことについてはあまり自信がありません。こういった、パイプ関連の動作について詳しく述べている文献は無いものでしょうか。

追記(01/15)
posted by bakemoji at 22:21| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年12月08日

プログラマ向けPowerShell本が出るみたい

年明けに「プログラマブル PowerShell 〜プログラマのための活用バイブル〜」という書籍が出るようです。著者は「Iron Pythonの世界」の著者でもある荒井さんです。副題や荒井さんのブログの紹介記事を読む限りではTips集というよりは文法書よりの内容のようです。

僕はPowerShellをプログラミング言語としていじって楽しんでいるので、タイトルにかなり惹かれるものがあります。ただ、同じように文法を詳しく解説している「PowerShell インアクション」とどの程度差別化されているかが気になります。購入するかどうかはそれ次第になりそうです。

ラベル:PowerShell
posted by bakemoji at 03:28| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年11月07日

PowerShell 2.0 CTP リリース

でたみたいですね。追加点、改良点の概要はWhat's New in CTP of PowerShell 2.0にあります。

ざっと読んだところ、1.0との互換性に注意しつつ機能強化が図られているようです。基本的には1.0のスクリプトは動作するようですが、新しいキーワード、変数、コマンドレットが追加されたので名前が衝突する場合はパースエラーになったり誤動作の原因になったりしそうです。

気になるのは新しく追加されたOut-GridViewコマンドレット(ソートなどが可能なGUIの表に出力)とGraphical PowerShell(グラフィカルなシェル。開発+実行環境?)が.NET Framework 3.0を要求することです。GUIにWPFを使用しているのでしょうか。

実験環境がないため試せませんが、追加点や改良点を見るになかなか良さそうではあります。ベータ版や正式版のリリースが楽しみです。

posted by bakemoji at 22:52| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年10月19日

PowerShellのヘルプについて その2

前回の続きです。Get-Helpコマンドレットはヘルプを表示しているわけではなく、オブジェクトを返します。コマンドレットのヘルプはMamlCommandHelpInfo、プロバイダのヘルプはProviderHelpInfo、言語のヘルプはstringにいくつかのPowerShellでのプロパティが追加されたものが返されます。

PS > $h = Get-Help Remove-Item
PS > $h.Name
Remove-Item
PS > $h.Synopsis
指定した項目を削除します。

などというように、ヘルプで表示される内容に個別にアクセスできます。

ですが、このヘルプオブジェクト、ProviderHelpInfoとMamlCommandHelpInfoの間で構造に一貫性があまり無いようです。ひつつ例を挙げます。

ProviderHelpInfoには頻出タスク別にコマンド例がいくつかあります。ProviderHelpInfoがTaskを複数持ち、TaskがExampleを複数持つという関係です。Exampleからは

PS > $ph = Get-Help FileSystem
PS > $ph.Tasks.Task[1].Title
ファイルとディレクトリ情報を取得する
PS > $ph.Tasks.Task[1].Examples.Example[0].Code
get-childitem
PS > $ph.Tasks.Task[1].Examples.Example[0].Introduction
現在のディレクトリにあるすべてのファイルとディレクトリを取得します。
既定では、Get-ChildItem の動作は再帰的ではありません。
PS > $ph.Tasks.Task[1].Examples.Example[0].Remarks
このコマンドを実行する際、現在のディレクトリにファイルとフォルダが存在している場合には、
System.IO.FileInfo オブジェクトと System.IO.DirectoryInfo オブジェクトという
2 種類のオブジェクトが返されることに注意してください。

のようにコマンド例とそれに対する説明、注釈などを取得できます。対して、MamlCommandHelpInfoのExampleからは

PS > $ch = Get-Help Remove-Item
PS > $ch.Examples.Example[0].Code
remove-item C:\Test\*.*
PS > $ch.Examples.Example[0].Introduction
C:\PS>
PS > $ch.Examples.Example[0].Remarks
このコマンドを実行すると、名前にドット (.) が含まれるファイルが C:\Test ディレクトリからすべて削除されます。
コマンドでドットを指定しているため、ディレクトリや拡張子のないファイルは削除されません。

のように、コマンド例とそれに対する説明を取得できますが、説明を取得できるプロパティがそれぞれRemarksとIntroductionというように異なっています。

他にも、この2つのオブジェクトにはちょっとした差異がいくつかあります。こういった差異が、独自にhtmlヘルプを作るときなど、オブジェクトを解析するときに足かせとなりそうです。

posted by bakemoji at 01:46| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年10月17日

PowerShellのヘルプについて

PowerShellでは、Get-Helpコマンドレットによりコマンドレット、プロバイダ、シェルや言語に関する事柄についてのヘルプを閲覧できます。Get-Helpコマンドレットで表示されるヘルプは情報量が豊富でかつ日本語化されているので非常に便利です。

PS > Get-Help Remove-Item
#Remove-Itemコマンドレットの説明が表示される
PS > Get-Help FileSystem
#FileSystemプロバイダの説明が表示される
PS > Get-Help about_if
#ifステートメントの説明が表示される

コマンドレットのヘルプについては-detailedスイッチを指定することでより詳細な情報を得られます。各パラメータの説明やいくつかの利用例も表示されるので、便利です。

さらに、上記のように言語の説明も表示できるので、例えばどんな演算子が使えるかなど文法で迷ったら、

PS > Get-Help about*

で表示される事柄一覧から演算子に関係ありそうなものを改めてGet-Helpコマンドレットで表示したり、

PS > Get-Help about* | where { $_ | Select-String "演算子" -quiet }

で各ヘルプファイルを「演算子」で検索して絞り込んだり出来ます。Select-Stringコマンドレットは入力されたファイル又は文字列から与えられた正規表現パターンにマッチする部分を探します。通常は見つかったらMatchInfoオブジェクトを返しますが、-quietスイッチを指定することでTrue/Falseを返すようにできます。

長くなってきたので次回に続きます。次回はGet-Helpコマンドレットの返すオブジェクトについてです。

posted by bakemoji at 18:49| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年09月21日

PowerShellにおけるメタプログラミングについて

PowerShellでメタプログラミング++C++; // 未確認飛行 C++ 管理人の日記)でメタプログラミングについて触れられています。なるほどなぁと読んでいたのですが、

メソッド呼び出しだけは $x.abs() みたいな書き方できないんですよね。誰か、何かいいアイディア持ってる人いないですかね。

とのこと。僕はPowerShell イン アクションを持っているのでそれを見れば答えは載っているのですが、腕試しにあえて見ないで書いてみました。

#classutil.ps1

$global:__class__ = @{}

function global:class([string] $name, [ScriptBlock] $definition) {
    $global:__class__[$name] = $definition
}

function global:new([string] $typeName) {
    function var([string] $name, $value = $null) {
        $this | Add-Member NoteProperty $name $value
    }

    function def([string] $name, [ScriptBlock] $method) {
        $this | Add-Member ScriptMethod $name $method
    }

    $definition = $global:__class__[$typeName]
    if (! $definition) {
        throw "$typeName is undefined."
    }

    $obj = New-Object PSObject

    $obj | Add-Member ScriptMethod "__def__" $definition
    $obj.__def__()
    $obj.psobject.members.remove("__def__")

    $obj
}

以下のように使います。

PS > .\classutil.ps1
PS > class rectangle {
>>     var width
>>     var height
>> 
>>     def area {
>>         $this.width * $this.height
>>     }
>> }
>> 
PS > $x = new rectangle
PS > $x.width = 30
PS > $x.height = 20
PS > $x.area()
600

以前触れたPSObjectの拡張を利用してメンバを追加しています。__def__のあたりが多少強引ですが、それなりに簡潔にはなったと思います。コンストラクタについては、スマートな書き方が思い浮かばなかったので華麗にスルーしました。

posted by bakemoji at 19:55| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

PowerShellにおけるスクリプトのドット化について

スクリプトは独自のスコープを持っており、スクリプト内部で定義した関数や変数はスクリプトの実行が終了したあとは破棄されます。ですが、例えばスクリプトに関数の定義を集めてライブラリを作成するなど、スクリプト終了後もスクリプト内の関数や変数を有効にしたい場合があります。そのようなときに利用できるのがスクリプトのドット化です。

スクリプトのドット化は、スクリプトのスコープをスクリプトが実行されたスコープに合わせます。確認のためにスクリプトdottest.ps1を作成し、通常の実行とドット化を比較します。

PS > @'
>> $x = 10
>> function hello($name) {
>>     "Hello, " + $name
>> }
>> 
>> hello Tom
>> '@ > dottest.ps1
>> 
PS > .\dottest.ps1
hello Tom
PS > $x
PS > hello John
用語 'hello' は、コマンドレット、関数、操作可能なプログラム、
またはスクリプト ファイルとして認識されません。
用語を確認し、再試行してください。
PS > . .\dottest.ps1
Hello, Tom
PS > $x
10
PS > hello John
Hello, John

このように、通常のスクリプトの実行ではスクリプト内で定義されている変数$xと関数helloはスクリプト外からは使用できませんが、ドット化ではスクリプト内で定義した変数や関数をスクリプト終了後も使用できています。

スクリプトのドット化はグローバルで実行するのではなくあくまでスコープを広げるだけです。

PS > @'
>> . .\dottest.ps1
>> 
>> $x
>> hello John
>> '@ > dottest2.ps1
>>
PS > .\dottest2.ps1
Hello, Tom
10
Hello, John
PS > hello Anne
用語 'hello' は、コマンドレット、関数、操作可能なプログラム、
またはスクリプト ファイルとして認識されません。
用語を確認し、再試行してください。

dottest.ps1をdottest2.ps1内でドット化していますが、dottest2.ps1をドット化していないためグローバルではdottest.ps1内の定義は利用できません。

posted by bakemoji at 09:24| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年09月17日

PowerShellの出力系コマンドレットのエンコーディングについて

この前出力系コマンドレットのエンコーディングで若干戸惑ったのでメモを記しておきます。

PowerShellのOut-Fileコマンドレット、Export-ClIXMLコマンドレットはデフォルトでUTF-16で出力するようで、Out-Fileコマンドレットを使用する出力リダイレクトもまたUTF-16で出力することになります。別にUTF-16で構わないといえば構わないのですが、ASCIIやShift-JISを前提としたコマンドプロンプト時代のツールの出力をいったんファイルに保存し、それを別のツールで利用する場合などに困ります。

そのようなときは出力リダイレクトではなくOut-Fileコマンドレットを直接利用することで、エンコード方式を指定してファイルに出力できます。

PS > "こんにちは世界" | Out-File -filePath hello.txt -encoding Default

-encodingパラメータに指定できる文字列はUnicode、UTF7、UTF8、ASCII、UTF32、BigEndianUnicode、Default、OEMで、PowerShell日本語版ではDefaultはShift-JISのようです。

Export-CsvコマンドレットだけはデフォルトではASCIIで出力するようで、日本語が文字化けします。これも-encodingパラメータを指定することで回避できます。

PS > $x = $x | Add-Member -passThru noteProperty Description "こんにちは世界"
PS > $x | Export-Csv -path test.csv
PS > type test.csv
#TYPE System.Int32
Description
???????
PS > $x | Export-Csv -path test.csv -encoding UTF8
PS > type test.csv
#TYPE System.Int32
Description
こんにちは世界

上手くいきました。それにしてもなぜExport-CsvコマンドレットだけASCIIで出力することにしたのでしょうか。謎です。

posted by bakemoji at 03:06| Comment(2) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年09月10日

PowerShellによるFizzBuzz問題の解法

そろそろブームも終わりそうなFizzBuzz問題をPowerShellで解いてみました。

switch (1..100) {
    { $_ % 15 -eq 0 } { "FizzBuzz"; continue }
    { $_ % 3 -eq 0 } { "Fizz" }
    { $_ % 5 -eq 0 } { "Buzz" }
    default { $_ }
}

何の面白みもないです。foreachとifではなくswitchを使っているのが特徴といえば特徴です。PowerShellでは配列もswich文に渡せますので、それを利用しています。

posted by bakemoji at 16:34| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年09月05日

PowerShellにおける列挙と列挙子について

C#ではIEnumerableインターフェースまたはIEnumerable<T>ジェネリックインターフェースを実装した型はforeach構文によって列挙可能です。PowerShellでは、以前説明したようにIEnumerableを実装した型がforeach構文、Foreach-Objectコマンドレットで列挙可能ですが、それに加えて列挙子、つまりIEnumeratorも列挙可能です。例を挙げましょう。

PS > foreach ($i in 1,2,3).GetEnumerator()) { $i }
1
2
3
PS > (1,2,3).GetEnumerator() | foreach { $_ }
1
2
3

文字列やハッシュテーブルはforeach構文やForeach-Objectコマンドレットでは列挙することは出来ませんが、列挙子を明示的に取得することで、列挙可能です。

PS > "Tom".GetEnumerator() | foreach { $_ }
T
o
m
PS > @{ "one" = 1; "two" = 2; "three" = 3 }.GetEnumerator() | foreach { $_ }

Name                           Value
----                           -----
two                            2
three                          3
one                            1
posted by bakemoji at 00:13| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年09月02日

PowerShellのGet-Memberコマンドレットについて

PowerShellでは、Get-Memberコマンドレットでオブジェクトのメンバを取得できます。

PS > $x = "Mojibake"
PS >$x | Get-Member


   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
Clone            Method                System.Object Clone()
CompareTo        Method                System.Int32 CompareTo(Object value),...
Contains         Method                System.Boolean Contains(String value)
CopyTo           Method                System.Void CopyTo(Int32 sourceIndex,...
...以下略

続いて、配列のメンバを調べてみましょう。

PS > $x = 1,2,3
PS > $x -is [Object[]]
True
PS > $x | Get-Member


   TypeName: System.Int32

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     System.Int32 CompareTo(Int32 value), System.Int32 Com...
Equals      Method     System.Boolean Equals(Object obj), System.Boolean Equ...
GetHashCode Method     System.Int32 GetHashCode()
GetType     Method     System.Type GetType()
GetTypeCode Method     System.TypeCode GetTypeCode()
ToString    Method     System.String ToString(), System.String ToString(IFor...

$xはObject[]なのに、Get-Memberコマンドレットは要素であるInt32のメンバを表示しています。どういうことでしょうか。確認のために、いろいろな型のオブジェクトを混在させた配列に対してメンバを取得してみましょう。

PS > $x = "Hello", 3.14, 256
PS > $x | Get-Member


   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
Clone            Method                System.Object Clone()
CompareTo        Method                System.Int32 CompareTo(Object value),...
...中略
   TypeName: System.Double

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     System.Int32 CompareTo(Object value), System.Int32 Co...
Equals      Method     System.Boolean Equals(Object obj), System.Boolean Equ...
...中略
   TypeName: System.Int32

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     System.Int32 CompareTo(Int32 value), System.Int32 Com...
Equals      Method     System.Boolean Equals(Object obj), System.Boolean Equ...
...以下略

どうやら、Get-Memberコマンドレットはパイプラインから配列を渡されると配列の各要素に対してユニークな型ごとにメンバを取得するようです。では、どうすれば配列のメンバを取得できるのでしょうか。

方法は2つあります。1つはGet-Memberコマンドレットの-inputObjectパラメータに明示的にオブジェクトを渡すという方法、もう1つはコンマ演算子を使う方法です。

PS > Get-Member -inputObject $x


   TypeName: System.Object[]

Name               MemberType    Definition
----               ----------    ----------
Count              AliasProperty Count = Length
Address            Method        System.Object& Address(Int32 )
Clone              Method        System.Object Clone()
...以下略

PS > ,$x | Get-Member
...出力は省略

コンマ演算子は以前触れたように、被演算子を要素としてもつ配列を生成します。Get-Memberはパイプラインからの入力を要素ごとに処理するので、配列の配列をパイプラインに流せば配列のメンバを取得できるというわけです。

posted by bakemoji at 00:21| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年08月20日

PowerShellにおけるオブジェクトの拡張について

オブジェクトの動的な拡張については、PythonやJavaScriptといった動的言語に慣れている方にはお馴染みですね。PowerShellでは、Add-MemberコマンドレットやPSObjectを利用してオブジェクトを拡張できます。まずは、Add-Memberコマンドレットを使用する例です。

PS > $x = 1
PS > $x | Add-Member ScriptMethod Greet { "Hello, my number is $this."  }
PS > $x.Greet()
[System.Int32] に 'Greet' という名前のメソッドが含まれないため、メソッドの呼び出しに失敗しました。

失敗してしまいました。Add-Memberコマンドレットを使用する場合、拡張したいオブジェクトがPSObjectかどうか(-is [PSObject])に注意が必要です。PSObjectならばいいのですが、PSObjectでない場合はAdd-MemberのpassThruスイッチを指定してAdd-Memberが値を返すようにしないと拡張したオブジェクトを使用できません。

PS > $x -is [PSObject]
False
PS > $x = $x | Add-Member -passThru ScriptMethod Greet { "Hello, my number is $this."  }
PS > $x.Greet()
Hello, my number is 1.
PS > $x -is [OSObject]
True

Add-Memberは入力が非PSObjectだった場合PSObjectでラップして拡張するので、新しく作られたPSObjectをpassThruスイッチを指定することで取得するのです。$xはPSObjectでラップされたので、さらにオブジェクトを拡張するときは-passThruは必要ありません。

つづいてPSObjectを利用してオブジェクトを拡張します。これには、PSObjectプロパティを使用します。PSObjectプロパティは特殊で、PSObjectでないオブジェクトに対してPSObjectプロパティを取得しようとすると、エラーにならずにそのオブジェクトがPSObjectでラップされます。例を見てみましょう。

PS > $x = 1
PS > $x -is [PSObject]
False
PS > $x.PSObject

Members             : {CompareTo, Equals, GetHashCode, ToString...}
Properties          : {}
Methods             : {CompareTo, Equals, GetHashCode, ToString...}
ImmediateBaseObject : 1
BaseObject          : 1
TypeNames           : {System.Int32, System.ValueType, System.Object}

PS > $x -is [PSObject]
True

最初は$xはただのInt32であるため$x -is [PSObject]はFalseを返しますが、一度PSObjectプロパティにアクセスしてからは$x -is [PSObject]がTrueを返すようになっていますね。

それでは、$xを拡張してみましょう。先ほどと同様に、Greetメソッドを追加します。

PS > $method = New-Object System.Management.Automation.PSScriptMethod `
>> Greet, { "Hello, my number is $this." }
>>
PS > $x.PSObject.Members.Add($method)
PS > $x.Greet()
Hello, my number is 1.

メソッドを追加できました。オブジェクトの拡張には基本的にはAdd-Memberコマンドレットを使用しますが、PSObjectプロパティを利用するやり方はプログラマには馴染みやすいかもしれませんね。

posted by bakemoji at 14:53| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年08月09日

PowerShellにおける列挙と.NETの列挙子について

PowerShellには、列挙子を利用したコレクションの列挙方法として制御文のforeachとForEach-Objectコマンドレットがあります。 基本的にIEnumerableインターフェイスを実装した型ならば列挙してくれますが、プログラマブルな趣味、もっぱらスクリプティングForEach-Objectって・・・でも述べられているように、文字列とハッシュテーブルは列挙してくれないようです。 「PowerShell イン アクション」によると、スカラー性を維持したいためだそうです。 また、制御文のforeachとForEach-Objectコマンドレットの違いとして、foreachが値の配列を一括で生成してからループするのに対して、ForEach-Objectは値を一つ生成するごとにループするそうです。 ForEach-Objectの方が大きなファイルの処理に向いていますね。

とりあえず、簡単な例でIEnumerableを実装すれば列挙可能かどうか実際に試してみました。C#で以下のクラスを作成し、dllにします。

using System;
using System.Collections;
using System.Collections.Generic;

namespace Mojibake
{
    public class Test : IEnumerable<int>
    {
        public IEnumerator<int> GetEnumerator()
        {
            for (int i = 0; i < 5; i++)
            {
                yield return i;
            }
        }
        
        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int i = 0; i < 5; i++)
            {
                yield return i;
            }
        }
    }
}

あとはPowerShellからdllをロードして実行します。

PS > [void][Reflection.Assembly]::LoadFile("C:\...\test.dll")
PS > $test = New-Object Mojibake.Test
PS > foreach ($i in $test) { $i }
0
1
2
3
4
PS > $test | foreach { $_ }
0
1
2
3
4

また、列挙にはIEnumeratorを利用するため、IEnumerator<T>のみを実装しているような場合は列挙してくれないようです。上記のクラスTestのIEnumerable.GetEnumerator()メソッドを以下のように変更します。

IEnumerator IEnumerable.GetEnumerator()
{
    throw new NotSupportedException("IEnumerable.GetEnumerator() is not supported.");
}

C#ではforeachで問題なく列挙できますが、先ほどと同様にPowerShellでロードして列挙を実行すると

PS > $test | foreach { $_ }
コレクションの列挙中に次の例外がスローされました: "IEnumerable.GetEnumerator() is not supported."。

このように、例外が発生してしまいます。まあ、PowerShellにおいてジェネリックな列挙に意味があるかというとあまりない気もしますが。

posted by bakemoji at 01:50| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年08月08日

PowerShellにおけるコレクションの平滑化とコンマ演算子について

PowerShellでは出力オブジェクトのコレクションが場合によって平滑化されるようです。

PS > $xs = (1, 2), (3, 4), (5, 6)
PS > $xs.length
3
PS > $xs | foreach { $_.length }
2
2
2

$xsは長さ2の配列を要素にもつ長さ3の配列です。では、ForEach-Objectコマンドレットで単純に各要素を返した結果を$ysに代入するとどうなるでしょうか。

PS > $ys = $xs | foreach { $_ }
PS > $ys.length
6

要素数が6とでました。代入に伴い、配列の配列が平滑化されてただの配列になっていますね。このほうが便利な場合もありますが、そうでないときもあります。構造を保ったまま代入するにはどうしたらよいでしょうか。

平滑化を阻止する方法の一つに、コンマ演算子を利用する方法があります。単項のコンマ演算子は、被演算子を要素として持つ配列を生成します。これをパイプラインオブジェクト$_につけることで配列を配列のまま受け取ることが出来ます。

PS > $ys = $xs | foreach { ,$_ }
PS > $ys.length
3
PS > $ys | foreach { $_.length }
2
2
2

配列の配列を$ysに代入することができました。

今度は、ForEachコマンドレットで配列を返すことを考えましょう。

PS > $xs = 1..5 | foreach { $_, ($_ * 2), ($_ * 3) }
PS > $xs.length
15
PS > $xs | foreach { [string]$_ }
1
2
3
2
4
6
3
6
9
4
8
12
5
10
15

$xsには配列の配列を代入したつもりですが、ここでもやはり平滑化されています。これもまたコンマ演算子を利用することで解決できます。

PS > $xs = 1..5 | foreach { ,($_, ($_ * 2), ($_ * 3)) }
PS > $xs.length
5
PS > $xs | foreach { [string]$_ }
1 2 3
2 4 6
3 6 9
4 8 12
5 10 15

$xsに正しく配列の配列が代入されました。

PowerShellは他言語経験者にとって学習しやすい言語だとは思いますが、それでもやはりこういったユーザを驚かせる動作はところどころ存在しますね。使いこなすうえで注意が必要です。

posted by bakemoji at 00:37| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

2007年08月05日

「Windows PowerShell イン アクション」について

Windows向けの新しいシェル、Windows PowerShellの、製作者自身の手による解説書です。現在第4章まで読み終わりました。

いわゆるレシピ集、Tips集といったものではなく、エンジンの動作や言語の仕様に関する解説書です。レシピはWebをあさればでてきますが、言語の詳細な解説はWebではなかなかないので、日本語で読めるのはうれしいですね。著者も述べていますが、「どういった機能があるか」だけでなく、「どうしてそうしたか」という設計の思想や理由についても書かれているのが素晴らしいです。

PowerShellに興味のある人はもちろんのこと、内部のメカニズムに興味のある人に特におすすめです。

Windows PowerShell イン アクション [イン アクションシリーズ]Windows PowerShell イン アクション [イン アクションシリーズ]
販売元 : Amazon.co.jp 本
価格 :
[タイトル] Windows PowerShell イン アクション [イン アクションシリーズ]
[著者] Bruce Payette
[種類] 大型本
[発売日] 2007..
>>Seesaa ショッピングで買う
posted by bakemoji at 02:43| Comment(0) | TrackBack(0) | PowerShell | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は90日以上新しい記事の投稿がないブログに表示されております。