preloader
5 July 2011 / #.net #Powercli #Powershell

Création d’un cmdlet Powershell [Partie4]

post-thumb

Suite de l’article Création d’un cmdlet Powershell [Partie3]

Dans l’article précédent, nous avons détaillé un code d’exemple permettant d’afficher la version d’un agent de PxeManager.

Dans ce billet, on détaillera comment on va pouvoir manipuler la sortie et l’affichage standard de notre cmdlet.

Customisation de la sortie des cmdlet

Toujours dans la série du Pourquoi ça fait ça ?, on s’est souvent demandé pourquoi tel ou tel cmdlet n’affichait pas leur sortie de la même manière en natif (ie sans utilisation de Format-Sort-, !).

Par exemple, lorsqu’on lance ces commandes :

Get-Process
Get-Process | Get-Member

ScreenShot159

On a un retour de base avec certaines colonnes, pourtant lorsqu’on regarde les membres du cmdlet, on voit bien que certaines propriétés ne sont pas visibles et que l’en-tête des colonnes ne correspond pas.

En fait cela n’est pas laissé au hasard (forcement :p) et tout est défini au lancement de la console par rapport au type d’objet à afficher.

Avec le cmdlet Get-Member on a le type de Get-Process :

TypeName: System.Diagnostics.Process

En fait, le comportement par défaut de l’affichage de la console Powershell (c’est à dire l’interprétation de la surcharge de ToString() ) se fait via des fichiers .ps1xml qui se trouvent dans le dossier $pshome

ScreenShot162

Pour l’exemple, on va regarder dans le fichier DotNetTypes.format.ps1xml pour trouver la définition du type System.Diagnostics.Process :

<View>
    <Name>process</Name>
    <ViewSelectedBy>
        <TypeName>System.Diagnostics.Process</TypeName>
    </ViewSelectedBy>
    <TableControl>
        <TableHeaders>
            <TableColumnHeader>
                <Label>Handles</Label>
                <Width>7</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>NPM(K)</Label>
                <Width>7</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>PM(K)</Label>
                <Width>8</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>WS(K)</Label>
                <Width>10</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>VM(M)</Label>
                <Width>5</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader>
                <Label>CPU(s)</Label>
                <Width>8</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader>
                <Width>6</Width>
                <Alignment>right</Alignment>
            </TableColumnHeader>
            <TableColumnHeader />
        </TableHeaders>
        <TableRowEntries>
            <TableRowEntry>
                <TableColumnItems>
                    <TableColumnItem>
                        <PropertyName>HandleCount</PropertyName>
                    </TableColumnItem>
                    <TableColumnItem>
                        <ScriptBlock>[int]($_.NPM / 1024)</ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <ScriptBlock>[int]($_.PM / 1024)</ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <ScriptBlock>[int]($_.WS / 1024)</ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <ScriptBlock>[int]($_.VM / 1048576)</ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <ScriptBlock>
                        if ($_.CPU -ne $())
                        {
                        $_.CPU.ToString("N")
                        }
                        </ScriptBlock>
                    </TableColumnItem>
                    <TableColumnItem>
                        <PropertyName>Id</PropertyName>
                    </TableColumnItem>
                    <TableColumnItem>
                        <PropertyName>ProcessName</PropertyName>
                    </TableColumnItem>
                </TableColumnItems>
            </TableRowEntry>
        </TableRowEntries>
    </TableControl>
</View>

Cet exemple comporte plusieurs parties qui vont définir l’affichage par défaut au niveau de la console PowerShell. Les vues définies dans ce fichier se décomposent généralement en 2 sections : le type de variable supporté et la partie configuration de l’affichage.

Dans notre exemple au dessus, nous avons la première section qui définie le type de la vue :

<ViewSelectedBy>
    <TypeName>System.Diagnostics.Process</TypeName>
</ViewSelectedBy>

Cette vue correspondra donc à l’objet de type System.Diagnostics.Process La configuration de l’affichage dans la console Powershell de ce type d’objet se fait dans la 2ème section qui est un TableControl. Ce tableau comportera des en-têtes (partie 1) et des valeurs (partie 2) :

Untitled-1

mais surtout le contrôle de l’affichage des ces valeurs.

Par exemple, on voit quand la feuille TableRowEntries (ie la définition des valeurs à afficher), on peut trouver plusieurs choses dont une partie <PropertyName>HandleCount</PropertyName> qui au final ne fait qu’afficher la propriété de l’objet sans modification, mais surtout on trouve des <ScriptBlock>[int]($_.NPM / 1024)</ScriptBlock> et là c’est génial puisqu’on va pouvoir injecter du code directement dans notre fichier de présentation, et là, c’est la classe ^^

Pour l’exemple sur Get-Process, on a la définition de la colonne CPU(s) qui est dans un ScriptBlock et qui contient le code suivant :

if ($_.CPU -ne $())
{
    $_.CPU.ToString("N")
}

On va modifier ce code par le suivant :

if ($_.CPU -ne $())
{
    if ($_.CPU -gt 1000)
    {
        [System.Console]::ForegroundColor = [System.ConsoleColor]::Red
        $_.CPU.ToString("N")
    }
    else
    {
        [System.Console]::ForegroundColor = [System.ConsoleColor]::DarkYellow
        $_.CPU.ToString("N")
    }
}

Ensuite on va rouvrir une nouvelle console Powershell et relancer Get-Process pour voir la différence :

Untitled-1

** est supérieure à 1000s.

Pour en revenir à notre exemple sur PXEManager, notre but n’est pas de modifier un existant mais de créer une nouvelle définition. On va donc créer un nouveau fichier PS1XML, le remplir et voir comment on l’intègre dans la console Powershell.

On va donc créer un fichier nommé PxeManagerDotNetTypes.format.ps1xml dont le contenu sera pour l’exemple :

<?xml version="1.0" encoding="utf-8" ?>
<Configuration>
    <ViewDefinitions>
        <View>
            <Name>pxeAboutInfo</Name>
            <ViewSelectedBy>
                <TypeName>pxeAboutInfo</TypeName>
            </ViewSelectedBy>
            <TableControl>
                <TableHeaders>
                    <TableColumnHeader>
                <Label>Name</Label>
                        <Width>40</Width>
                    </TableColumnHeader>
                    <TableColumnHeader>
                        <Label>Version</Label>
                        <Alignment>Center</Alignment>
                        <Width>30</Width>
                    </TableColumnHeader>
                </TableHeaders>
                <TableRowEntries>
                    <TableRowEntry>
                        <TableColumnItems>
                            <TableColumnItem>
                                <PropertyName>Name</PropertyName>
                            </TableColumnItem>
                            <TableColumnItem>
                                <ScriptBlock>[string]($_.version.Major)+"."+[string]($_.version.Minor)+"."+[string]($_.version.Build)</ScriptBlock>
                            </TableColumnItem>
                        </TableColumnItems>
                    </TableRowEntry>
                 </TableRowEntries>
            </TableControl>
        </View>
    </ViewDefinitions>
</Configuration>

Ce fichier ne définit que 2 colonnes Name et Version, la colonne Name n’affichant que la propriété Name et la colonne Version affichant dans l’ordre le numéro de version en explosant la propriété version de l’objet pxeVersion.

ScreenShot157b

Pour l’intégrer à la console Powershell, il suffit de passer par la commande Update-FormatData (MSDN disponible sur http://technet.microsoft.com/en-us/library/dd347564.aspx). Ce cmdlet est utilisé pour mettre à jour le formatage par défaut des types d’objets. Dans notre cas, il va créer un formatage pour le type pxeAboutInfo suivant le fichier PS1XML que nous lui avons fournit :

ScreenShot163

Cela nous permet donc de maitriser comment notre cmdlet nouvellement créé sera afficher par défaut dans la console.

Voilà qui clot cette série d’article sur la création de cmdlet perso.

Encore un grand merci pour Max qui nous a fait ici un tool incontournable !


> Frederic MARTIN