V minulém díle jsme vytvářeli textový výstup pomocí operátoru formátování. Dnes se podíváme na opačný pól – vytvoříme si vlastní objekt z daného textového výstupu.

Již několikrát jsem zmiňoval, že PowerShell je postaven nad objekty a s objekty dokáže velice efektivně pracovat. Problém občas nastává, pokud vytváříte vlastní skript nebo funkci a na výstupu chcete mít vlastní objekty. V tomto případě se hodí cmdlet New-Object. Tento cmdlet má jeden povinný parametr, TypeName, tento parametr určuje typ objektu – ve většině našich případů zde zadejte PSObject. Tím vytvoříte objekt typu System.Management.Automation.PSCustomObject – bohužel vytvoření objektu bez vlastností nebo metod nedává moc smysl:

PS C:\> New-Object -TypeName PSObject

PS C:\> $a = New-Object -TypeName PSObject
PS C:\> $a

PS C:\> $a.GetType().FullName
System.Management.Automation.PSCustomObject
PS C:\> $a | Get-Member

TypeName: System.Management.Automation.PSCustomObject
Name        MemberType Definition
----        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
ToString    Method     string ToString()

K tomuto objektu můžeme vlastnosti přidávat později za běhu skriptu, například takto:

[20]: $a = $a | Add-Member -MemberType NoteProperty -Name Vlastnost -Value 'Nejaky text' -PassThru
[21]: $a

Vlastnost
---------
Nejaky text

[22]: $a | Get-Member

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Vlastnost   NoteProperty System.String Vlastnost=Nejaky text

Pomocí cmdletu Add-Member jsme k původnímu objektu přidali vlastnost Vlastnost a přiřadili jsme jí určitou hodnotu. I když je tento způsob vytváření objektů možný, existuje naštěstí lepší volba. Tou je použití parametru Parameter při vytváření objektu pomocí New-Object. Pojďme si vytvořit nový objekt:

PS C:\> New-Object -TypeName PSObject -Property @{ a=1; b=2; c=3 }

a      b      c
-      -      -
1      2      3

PS C:\> $b = New-Object -TypeName PSObject -Property @{ a=1; b=2; c=3 }
PS C:\> $b | Get-Member

   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
a           NoteProperty System.Int32 a=1
b           NoteProperty System.Int32 b=2
c           NoteProperty System.Int32 c=3

Parameter očekává hash tabulku, jejíž dvojice klíč-hodnota se stanou vlastností a její hodnotou v novém objektu. Hash tabulku si samozřejmě můžeme vytvořit předem a pak ji uvést jako hodnotu parametru.

PS C:\> $prop = @{
>> a=1
>> b=2
>> c=3
>> }
>> 
PS C:\> $prop

Name      Value
----      -----
a         1
b         2
c         3

PS C:\> $prop.GetType().FullName
System.Collections.Hashtable
PS C:\> New-Object -TypeName PSObject -Property $prop

a      b      c
-      -      -
1      2      3

Vidíte, že tvorba nového objektu proběhla ve dvou krocích. Nejdříve jsme v hash tabulce určili, jaké vlastnosti bude nový objekt obsahovat a ve druhém kroku jsme tyto vlastnosti použili při vytváření objektu typu PSObject. Tím jsme si oddělili „starosti“ s přemýšlením o vlastnostech objektu. Zkusme jednoduchý příklad.

function Get-TNObject
{
    param(
        $ComputerName = $env:COMPUTERNAME
    )

    $os = Get-WmiObject -ComputerName $ComputerName -Class Win32_OperatingSystem

    $prop = @{
        ComputerName = $ComputerName
        OSName       = $os.Caption
        OSVersion    = $os.Version
        ScanTime     = Get-Date
    }

    New-Object -TypeName PSObject -Property $prop

}

Funkce Get-TNObject očekává na vstupu jméno počítače, který testujeme. Uvnitř funkce zavoláme cmdlet Get-WmiObject a zjistíme informace o operačním systému. Do hash tabulky poté vložíme informace o jménu počítače, jeho operačním systému a verzi a také datum, kdy jsme tento sken spustili. Na posledním řádku poté vytváříme objekt. Tento objekt je dále pouštěn do roury a výsledky funkce tam můžeme zpracovat jiným cmdletem. Použití je následující:

PS C:\> Get-TNObject

OSVersion  ScanTime            OSName                        ComputerName
---------  --------            ------                        ------------
5.1.2600   05-Jun-12 11:29:14  Microsoft Windows XP Profe... MAKOVEC-40

PS C:\> Get-TNObject localhost

OSVersion  ScanTime            OSName                         ComputerName
---------  --------            ------                         ------------
5.1.2600   05-Jun-12 11:29:18  Microsoft Windows XP Profe...  localhost

PS C:\> Get-TNObject localhost | Format-Table -AutoSize

OSVersion  ScanTime            OSName                             ComputerName
---------  --------            ------                             ------------
5.1.2600   05-Jun-12 11:29:32  Microsoft Windows XP Professional  localhost

Pokud bychom chtěli zpracovávat více počítačů, můžeme použít například cmdlet ForEach-Object.

PS C:\> 'localhost',$env:COMPUTERNAME,'makovec-40','192.168.100.100' |% { Get-TNObject $_; Sleep 2 }

OSVersion ScanTime            OSName                         ComputerName
--------- --------            ------                         ------------
5.1.2600  05-Jun-12 11:34:59  Microsoft Windows XP Profe...  localhost
5.1.2600  05-Jun-12 11:35:01  Microsoft Windows XP Profe...  MAKOVEC-40
5.1.2600  05-Jun-12 11:35:03  Microsoft Windows XP Profe...  makovec-40
5.1.2600  05-Jun-12 11:35:05  Microsoft Windows XP Profe...  192.168.100.100

Pro kontrolu jsem přidal čekání dvě vteřiny, aby bylo vidět, že ScanTime se nám opravdu mění v závislosti na volání funkce. Jak jsem již zmiňoval – výstupem jsou objekty a můžeme s nimi dále libovolně pracovat.

PS C:\> 'localhost',$env:COMPUTERNAME, 'makovec-40','192.168.100.100' |% { Get-TNObject $_ } | Sort ComputerName

OSVersion ScanTime            OSName                         ComputerName
--------- --------            ------                         ------------
5.1.2600  05-Jun-12 11:38:28  Microsoft Windows XP Profe...  192.168.100.100
5.1.2600  05-Jun-12 11:38:28  Microsoft Windows XP Profe...  makovec-40
5.1.2600  05-Jun-12 11:38:28  Microsoft Windows XP Profe...  MAKOVEC-40
5.1.2600  05-Jun-12 11:38:28  Microsoft Windows XP Profe...  localhost

PS C:\> 'localhost',$env:COMPUTERNAME, 'makovec-40','192.168.100.100' |% { Get-TNObject $_ } | Sort ComputerName | ft -AutoSize

OSVersion ScanTime            OSName                             ComputerName
--------- --------            ------                             ------------
5.1.2600  05-Jun-12 11:38:43  Microsoft Windows XP Professional  192.168.100.100
5.1.2600  05-Jun-12 11:38:43  Microsoft Windows XP Professional  makovec-40
5.1.2600  05-Jun-12 11:38:43  Microsoft Windows XP Professional  MAKOVEC-40
5.1.2600  05-Jun-12 11:38:43  Microsoft Windows XP Professional  localhost

Tato technika vytváření objektů za běhu funkce nebo skriptu je velice užitečná. Vzhledem k tomu, že v PowerShellu pracujeme s objekty velice často, byla by škoda, pokud by vaše funkce/skripty pouze posílaly textový výstup pomocí Write-Host. Až budete příště zpracovávat nějaká data, vzpomeňte si na New-Object a zkuste jej využít.

- David Moravec