[PowerShell]使ってみた

いつの間にかWindowsにいた「PowerShell」。何者?

OSはもっぱらwindowsを使っているが、コマンド自動実行といえばいまだにバッチ(*.bat)だと思ってた。
仕事で唐突に「PowerShell」なるものを使う機会があったので、いろいろいじってみた。
※そういえば、遠い昔にWSHとか使って気もするが、なんかいまいちだなーで終わった気がする。

1.Linuxっぽい

ちょっとだけLinuxをいじったことがあるけど、「bashに似ているな」が最初の感想。
たまたま見たシェルがパイプラインを多用していたのが原因かな。
その書き方が推奨なのかどうかはわからないけど、
処理順に書いてパイプラインでつないでもいいし、引数で受け取ってもいいみたい。

2.OS標準の開発環境がある

バッチみたいにテキストエディタでぽちぽちかと思いきや、
「Windows PowerShell ISE」というOS標準の開発環境があった(Windows10)。
インテリセンスもあるし、ブレークポイントも使えるので、それなりに便利。

3.配列のクセがすごい

多次元配列を使ったのだけど、

function setary
{
    $arytmp = @()
    $arytmp += ,@('elem1', 'val1')
    return $arytmp
}
[String[][]]$ary = setary
Write-Host "=setary="
$ary | foreach -Process { Write-Host ("cnt[" + $_.Count + "],val[" + $_ + "]") }

function setary2
{
    $arytmp = @()
    $arytmp += ,@('elem1', 'val1')
    $arytmp += ,@('elem2', 'val2')
    return $arytmp
}
[String[][]]$ary = setary2
Write-Host "=setary2="
$ary | foreach -Process { Write-Host ("cnt[" + $_.Count + "],val[" + $_ + "]") }

この「setary」と「setary2」の結果がなぜか異なる。
要素1個か?要素2個か?の違いだけなんだけど。
以下は出力結果。

=setary=
cnt[1],val[elem1]
cnt[1],val[val1]
=setary2=
cnt[2],val[elem1 val1]
cnt[2],val[elem2 val2]

理由も調べてみたんだけど、わかるようなわからないような。
「List型を使え!」とかも散見。

List型?で気づいたのが次。

4.クラスが使える

使えるなんて全く思ってなかっただけにびっくり。
継承も可能。残念ながらインタフェースや抽象クラスは無いみたい。

オブジェクト指向のサンプル作ってみた。

# ファイルの内容を一定の形に整形して返却する関数
# (1) クラス関数(static)「isValid」で使用するクラスを候補から決定
# (2) 「(1)」のインスタンス生成
# (3) 「(2)」のクラス関数「execute」を実行
# (4) 「(3)」の戻り値を返却
function readFile([String]$filePath)
{
    [OutputType([System.Collections.Generic.List[readResult]])]

    # 読み取りクラス候補群
    [Object[]]$readLogClasses = @([readLogSimple], [readLogSpecial], [readLogCsv])

    # 読み取りに使用するクラスのインスタンス
    [abstReadLog]$readLogObj = $null

    # 使用する読み取りクラスを探索
    foreach ($readLogC in $readLogClasses)
    {
        if ( $readLogC::isValid($filePath) )
        {
            $readLogObj = $readLogC::new()
            break
        }
    }

    # 結果を取得して返却
    return $(if($null -eq $readLogObj){ New-Object 'System.Collections.Generic.List[readResult]' }else{ $readLogObj.execute($filePath) })
}

# なんちゃって抽象クラス
class abstReadLog
{
    # 使用可否判定(抽象メソッド)
    [bool] static isValid([String]$filePath) { return $false }

    # コンストラクタ
    abstReadLog()
    {
        $this.otherInit()
    }

    # その他初期化(抽象メソッド)
    hidden [void]otherInit() { }

    # 読込結果作成(抽象メソッド)
    hidden [readResult]createReadResult([String]$filePath, [int]$remban, [exeTime]$startElem, [exeTime]$endElem) { return $null }

    # 読込実行
    [System.Collections.Generic.List[readResult]]execute([String]$filePath)
    {
        foreach( $lineFileData in Get-Content $filePath )
        {
            ~なんやかんや~
            $readResultElem = $this.createReadResult($filePath, ($readResultList.Count + 1), $startElem, $endElem)
            $readResultList.Add($readResultElem)
        }

        return $readResultList
    }
}

# 子クラス1
class readLogSimple : abstReadLog
{
    # 使用可否判定
    [bool] static isValid([String]$filePath)
    {
        [bool]$judge = $false
        ~なんやかんや~
        return $judge
    }

    # その他初期化
    hidden [void]otherInit() { }

    # 読込結果作成
    hidden [readResult]createReadResult([String]$filePath, [int]$remban, [exeTime]$startElem, [exeTime]$endElem)
    {
        [String]$taskName = $startElem.taskName + "[" + $startElem.sts + "/" + $endElem.sts + "]"
        return [readResult]::new($filePath, $remban, $taskName, $startElem.exeDateTime, $endElem.exeDateTime)
    }
}

5.感想

クラスが使えるというので、いろいろいじってたら、楽しくなってきた。
ただ、暗黙型変換はどうしても慣れない。型を宣言したくなってしょうがない。

バッチで小難しいことをやろうとすると、ぐちゃぐちゃになりがちだけど、
PowerShellだと、それなりにきれいなコードでかなりのことができそう。
使用頻度は高まってくるのかな?