hakeの日記

Windows環境でプログラミングの勉強をしています。

PowerShell - 独自オブジェクトの作成

PSObjectにプロパティやメソッドを追加していくことで独自のオブジェクトが作成できます。

# Hashでプロパティを追加
$hash = @{name = "Taro"; age = 20}
$obj = New-Object PSObject -Property $hash

# Add-Memberでプロパティを追加
$obj | Add-Member -MemberType NoteProperty -Name str -Value "Hello"
$obj | Add-Member -MemberType ScriptProperty -Name talk `
            -Value {Write-Host "$($This.str), My Name is $($This.name)."} 

# プロパティのgetter/setterを追加
Add-Member -InputObject $obj `
           -MemberType ScriptProperty -Name nenrei `
           -Value { $this.age } `
           -SecondValue {
                param($age)
                $This.age = $age
             } 

# メソッドを追加
Add-Member -InputObject $obj `
           -MemberType ScriptMethod -Name greeting `
           -Value {
                param($str)
                $This.str = $str
             } 



$obj.GetType()
# IsPublic IsSerial Name                    BaseType
# -------- -------- ----                    --------
# True     False    PSCustomObject          System.Object

$obj.name           # Taro
$obj.age            # 20
$obj.str            # Hello
$obj.talk           # Hello, My Name is Taro.

# プロパティを直接変更
$obj.name = "Jiro"
$obj.talk           # Hello, My Name is Jiro.

# プロパティを直接変更
$obj.age = 25
$obj.age            # 25

# プロパティsetterで変更
$obj.nenrei = 30
$obj.nenrei         # 30

# プロパティをメソッドで変更
$obj.greeting("Good Morning")
$obj.talk           # Good Morning, My Name is Jiro.

PowerShell - 例外処理(throw)

意図的に例外を発生させる場合にはthrowを使用する。

try { 
    "try節です"
    throw "例外発生"
}
catch [Exception] {
    "catch節です"
}
finally {
    "finary節です"
}
PS C:\> $error[0].CategoryInfo

Category   : OperationStopped
Activity   : 
Reason     : RuntimeException
TargetName : 例外発生
TargetType : String

例外として任意のオブジェクトも渡せる

try { 
    "try節です"
    throw New-Object regex ".."
}
catch [Exception] {
    "catch節です"
}
finally {
    "finary節です"
}
PS C:\> $error[0].CategoryInfo

Category   : OperationStopped
Activity   : 
Reason     : RuntimeException
TargetName : ..
TargetType : Regex


PS C:\> $error[0].TargetObject.GetType()

IsPublic IsSerial Name                    BaseType
-------- -------- ----                    --------
True     True     Regex                   System.Object

PowerShell - 関数(値渡しと参照渡し)

参照渡しの場合、呼び出し側の引数に[ref]をつける、また引数に括弧がないとエラーになった。
関数定義の仮引数の[ref]は無くても良い、関数内では仮引数のValueプロパティにアクセスする。

# 値渡し
function byVal($arg){
    $arg = "Good Bye"
    Write-Host "in Function : $arg"
}

# 参照渡し
function byRef([ref]$arg){
    $arg.Value= "Good Bye"  # Valueプロパティにアクセス
    Write-Host "in Function : $($arg.Value)"
}


$a = "Hello"
$a
byVal $a      # 値渡し
$a            # Hello


$a = "Hello"
byRef([ref]$a) # 参照渡し
$a             # Good Bye

PowerShell - 関数(パイプ)

パイプで渡されるてくるデータの処理用の関数。
開始処理はbeginブロック、終了処理はendブロック、渡されてくるデータの処理はprocessブロックに記述する。渡されてきたデータは変数$_に格納されている。
returnで値を返すと関数の出力になる。

even.ps1

function even {
    begin{ $cnt = 0 }
    process {
        $cnt++
        if($_ % 2 -eq 0){
            return "${cnt}番目のデータ:$_"
        } else {
            return
        }
    }
    end{ return "終了" }
}

実行結果

PS C:\> . C:\even.ps1

PS C:\> 1..10 | even
2番目のデータ:2
4番目のデータ:4
6番目のデータ:6
8番目のデータ:8
10番目のデータ:10
終了

PowerShell - 関数(可変長引数)

引数は変数$argsという配列にわたされる。

test.ps1

function foo {
    $args.GetType()

    foreach( $i in $args ) {
        Write-Host $i
    }
}

結果

PS C:\> . C:\test.ps1  # 関数読み込み
PS C:\> foo 1 2 3

IsPublic IsSerial Name                                     BaseType                    
-------- -------- ----                                     --------                    
True     True     Object[]                                 System.Array                
1
2
3

PS C:\> foo "a" "b" "c"

IsPublic IsSerial Name                                     BaseType                    
-------- -------- ----                                     --------                    
True     True     Object[]                                 System.Array                
a
b
c

PowerShell - 再帰関数

function fact($i) {
    if($i -eq 0) { 
        return 1
    } else {
        return $i * (fact ($i - 1))
    }
}

fact 5 # 120

2017/1/28追記 再帰回数に限界がある模様

Win10 PowerShell 5.1の環境で、以下の関数fooの引数が1000の場合は正常終了しましたが、5000の場合はエラーになりました。

function foo {
    param($i)

    #Write-Host $i
    if($i -eq 0){return 1}
    return (foo($i - 1)) + 1
}


Write-Host (Get-Date)
Write-Host "Result: $(foo(5000))" 
Write-Host (Get-Date)
PS C:\> C:\powershell\test.ps1
2017/01/28 21:47:03
Result: 1001
2017/01/28 21:47:04

PS C:\> C:\powershell\test.ps1
2017/01/28 21:47:18
呼び出しの深さのオーバーフローのため、スクリプトが失敗しました。
    + CategoryInfo          : InvalidOperation: (0:Int32) []、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : CallDepthOverflow