PowerShell では、事前にフォーマットされた出力を解析するにはどうすればよいですか?

PowerShell では、事前にフォーマットされた出力を解析するにはどうすればよいですか?

テキストベースの出力を取得し、それを列としてクエリできる動的オブジェクトにパイプする既存のツールはありますか?

具体的には、私は次のように呼び出します。

query session /server:MYSERVER

.. 出力しています..

SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE 
services                                    0  Disc                        
console           Jon                       1  Active  

これは、列の条件に基づいてこの出力を処理するという DevOps ロールでの私の最初のタスクですが、foreach などにパイプする試行を何度も行った後、すべてが複数行の文字列であることに気付きました。

私が期待していたのは次のようなものでした。

query session /server:MYSERVER | foreach `
{ `
    if ($_.Username -eq "Jon") `
    {`
        custom-action $_.ID `
    } `
}

(ユーザー名を必ずしも評価する必要はありません。また、ユーザー名だけを評価する必要もありません。この例ではそうしているだけです。)

明らかにこれは機能しません。なぜなら、これは...

query session /server:192.168.1.103 | foreach { echo $_.GetType() }

.. これを出力します..

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

私が見つけた唯一の解決策は、String.Substring() を使用して手動で列を抽出することです。これを自動化する PowerShell ツールがあることを期待していました。

[[これは例です。]] ですが、一部の列は空白で、固定幅の列は互いに同じ幅ではないため、これを解析するには、そこにある例よりもはるかに手作業が必要になります。Powershell のバージョン更新で、より優れたツールが提供されることを期待していました。

Windows Server 2012 R2 (PowerShell 4 搭載) を使用します。

答え1

何も組み込まれていません。

ただし、各列の定義 (名前、開始位置、長さ、タイプなど。長さを事前に決定できない場合は、列を区切るための代替基準など) を受け取り、カスタム オブジェクトを作成するヘルパー (もちろん、簡単に再利用できるようにモジュール内に) を作成することはそれほど難しくありません。

答え2

PowerShell 以前のユーティリティの多くは、WMI などのさまざまな基礎となる Windows API を照会します。テキスト解析を詳しく調べる前に、ユーティリティが情報を取得する場所を見つけて、そこから始めることをお勧めします。あなたの特定のケースでは、実際に Win32_LogonSession WMI クラスを照会していても不思議ではありません。このクラスは PowerShell を使用して簡単に列挙でき、適切にフォーマットされたオブジェクトが返されます。このアプローチはすべての状況で機能するとは限りませんが、少なくとも開始するには良い場所です。

$users = get-wmiobject -query "Select * from Win32_LogonSession"

少しグーグルで検索してみると、これはあなたが必要としている機能として有望なようです: get-loggedonuser 関数

答え3

これはリチャードの返答に対するコメントです。

私が思いついたのはこれです。すべての行(最初の行の $head を除く)で foreach{} を実行し、この関数に渡します。

Function ParseFixedWidthCols($head,$line) {
    $colnamematches = $head | select-string "(\s*\w+\b\s*)" -allmatches | foreach { $_.matches }
    $cols = @()
    for ($ci=0; $ci -lt $colnamematches.Count; $ci++) {
        $col = $colnamematches[$ci].Value
        $cols += $col
    }
    $col = $cols[0]
    $ret = New-Object PSObject
    $cc = 0
    for ($ci=0; $ci -lt $cols.Count; $ci++) {
        $value = $line.Substring($cc, $cols[$ci].Length)
        $ret | Add-Member -MemberType NoteProperty -name $cols[$ci].Trim() -value $value.Trim()
        $cc += $cols[$ci].Length
    }
    return ret;
}

関連情報