mercredi 15 août 2012

le verbe "GET" dans les fonctions et les cmdlets

salut,

le verbe "GET" est l'un des verbes autorisés dans le nommage des fonctions et cmdlets
Get-Verb -verb get
on peux omettre ce verbe pour toutes les cmdlets et fonctions:
childitem c:\
psdrive
hotfix
sauf les cmdlets get-command et get-process ne supportent pas cette omission parceque le mot "process" est un mot reservé (help keywords) et "command" est vu par powershell comme etant le vieux shell "command.com" il est à noter aussi que les fonction ayant comme nom un mot reservé seront traiter comme étant des mots reservés et non pas comme étant des fonctions
man key | Select-String "\s{5,}about_*"
et s'il s'agissait d'un nom d'un executable alors l'interpréteur powershell traitera cette fonction omme étant une commande externe et non pas une fonction
function get-cmd {1}

ou bien:

function get-Function {1}
le revert de la médaille dans tout ça et qu'on ne pourra plus bénéficier de la puissance de la complétion de powershell
Service -n[TAB] alg
un autre désavantage de l'utilisation de ces commandes sans le verbe "GET", est que parfois avec la commandes get-help on se retrouve devant des resultats inatendus
PS D:\> help verb

NOM
    Write-Verbose
...
.....
voici une petite fonction qui s'appuie sur les mots reservé et teste si une commande est valide ou pas:
#############################################################
#
#  PS D:\> Test-CommandValidation -command get-process | fl
#            VerbNounConvention : True
#            ReservedKeyWords   : True
#            VerbConvention     : True
#
#############################################################
function Test-CommandValidation {
   param($Command)
   $keys = man key | 
             Select-String "(\S+)(?=\s{5,}about_*)" | 
                  select -expand Matches | 
                        select -expand value
   $verbNounConvention = $verbconvention = $reservedkeywords = $false
   $verb,$noun = $Command.Split('-')
   if($noun) {
      $verbNounConvention = $true
      if( (get-verb $verb) ) { $verbconvention = $true } 
      if($keys -contains $noun) { $reservedkeywords = $true }
   }
   else {
       $reservedkeywords = $verbconvention = $null
   }
    new-object PSObject -prop @{
       VerbNounConvention = $verbNounConvention
       VerbConvention = $verbconvention
       ReservedKeyWords = $reservedkeywords
    }
}


mardi 17 juillet 2012

Watch-Process

salut,

Parceque la gestion des processus est vitale dans la vie d'un administrateur, powershell nous a offert 5 cmdlet complémentaires:

                   
Get-Process
Stop-Process        
Wait-Process 
Debug-Process
Start-Process      
le visionnage des process se fait simplement par "Get-Process" l'idéee de Watch-Process est d'étendrre les capacitées de 'Get-Process' pour monitorer un ou plusieurs process par un intervalle de temps personnalisable, en local ou à distance:

function Watch-Process {
<#

.ForwardHelpTargetName Get-Process
.ForwardHelpCategory Cmdlet

#>
[CmdletBinding(DefaultParameterSetName='Name')]
param(
    [Parameter(ParameterSetName='Name', Position=0, ValueFromPipelineByPropertyName=$true)]
    [Alias('ProcessName')]
    [ValidateNotNullOrEmpty()]
    [System.String[]]
    ${Name},

    [Parameter(ParameterSetName='Id', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
    [Alias('PID')]
    [System.Int32[]]
    ${Id},

    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [Alias('Cn')]
    [ValidateNotNullOrEmpty()]
    [System.String[]]
    ${ComputerName},

    [ValidateNotNull()]
    [Switch]
    ${Module},
   
     [Switch]
     ${Continous},
    
     [int32]
     ${Seconds}=0,

    [Alias('FV','FVI')]
    [ValidateNotNull()]
    [Switch]
    ${FileVersionInfo},

    [Parameter(ParameterSetName='InputObject', Mandatory=$true, ValueFromPipeline=$true)]
    [System.Diagnostics.Process[]]
    ${InputObject})

begin
{
    try {
        $outBuffer = $null
        if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
        {
            $PSBoundParameters['OutBuffer'] = 1
        }
       
        $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Management\Get-Process', [System.Management.Automation.CommandTypes]::Cmdlet)
        'Seconds','Continous' | foreach { $null=$PSBoundParameters.Remove($_) }
        $scriptCmd = {& $wrappedCmd @PSBoundParameters }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    } catch {
        throw
    }
}

process
{
       if($Continous) {
          while($true) {
              Start-Sleep -Seconds $Seconds
              Get-Process @PSBoundParameters
          }
       }
     
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
}

end
{

    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}

}
on peux l'utiliser ainsi:
PS> Watch-Process -Name n* -Continous -Seconds 2
PS> Watch-Process note*
PS> Watch-Process -Name n* -Continous

jeudi 3 mai 2012

String.Match( why not ? )

salut, l'un des points fort de PowerShell c'est son extensibilité et sa flexibilité, prenons par exemple, les fichiers de configuration de types étendus, ces fichiers permettent d'ajouter des membres dynamiques ou statiques aux objets telques des propriétés ou des méthodes,..etc l'ajout de ces membres ce fait grâce à la cmdlet Update-TypeData qui va charger notre fichier de configuration en mémoire puis ajoutera nos membres dans la session active. voici un petit exemple:


    
        System.String
        
            
                Matches
                
            

           
                Match
                
            

        
    

ce fichier de configuration va ajouter au type system.string deux nouvelles méthodes Match et Matches la première méthode va matcher la première reference de la chaine de caractères, et la deuxième va matcher toutes les références trouvée.. voius devez avant tout charger ce fichier en mémoire:
PS> Update-TypeData MyType.ps1xml
ensuite on pourra faire des choses du genre:
PS> # premier test
PS> Get-Content file.txt | foreach { $_.matches('KB\d{5,}') }
PS>
PS> $s = 'aeejkdkk55d'
PS> $p = '^.'
PS> $s.Match($p)
PS>

mercredi 4 avril 2012

le pipeline... comment en tirer profit ?

salut, l'une des plus belle choses dans powershell est son méchanisme révolutionnaire de pipeline et plus précisemment le méchanisme de liaison entre la sortie d'une commande et l'entrée d'une autre commande , en fait powershell permet de lier entre la valeur et/ou la propriété de l'objet de sortie avec le(s) paramètres de la commande d'entrée...ceci va nous permettre non seulement de rendre le pipeline entre les commandes "fluide" et "puissant" mais aussi de construire nos propres objets personnalisés si la liaison entre les commandes du pipeline n'est pas "permise" on va prendre un exemple facile...nous avons un fichier machines.txt contenant le nom de quelques machines:
C:\> cat ./machines.txt

win8
srv-9
localhost
nous voulons parcourir tous ces posts et afficher tous les services en marche pouvant être stopper...nous avons deux méthodes pour traiter ce problème 1- la façon "traditionnelle" 2- la façcon "powershell way" 1- la façon traditionnelle: pas de magie, c'est verbeux déclaratif et surtout fastidieux pour les tâches rapides d'administrations.
$computers=cat ./machines.txt
foreach($computer in $computers) {
  get-service -computer $computer | where {$_.status -eq 'running' -and $_.canstop -eq $true}
}
2- powershell way: c'est magique, concit, direct et rapide à écrire:
cat ./machines.txt | ConvertFrom-Csv -h 'computername' | gsv | ? {$_.status -eq 'running' -and $_.canstop}
tout le secret reside dans le méchanisme de binding entre les commandes: get-service à un paramètre "-computerName" et il accepte l'entrée du pipeline en mode "BypropertyName" tout ce qu'on fera par la suite c'est de générer un objet ayant comme propriété 'computerName' et ...c'est tout..c'est pas magique ça

samedi 31 mars 2012

Clear-PSSession : comment nettoyer notre environement

salut, les "entités" qui prennent de la place dans la mémoires comme les fonctions-filtres-variables-alias-modules-psessions-..etc peuvent parfois encombré notre session..pour remedier à ce problème voici une petite fonction 'Clear-PSSession' qui permet de nettoyer complètement notre environement et en plus s'auto-charge automatiquement:
if($Host.Name -ne 'ConsoleHost') {
  write-error "ce module ne fonctionne que sous le terminal powershell"
  return
}

$Script:CurrentProcess = $pid
$Script:MyCmd = $MyInvocation.MyCommand.Definition

function Clear-PSSession {
   powershell -noexit -c "stop-process $($Script:currentProcess);import-module '$($Script:MyCmd)'"
}

Export-ModuleMember -Function *  
exemple d'utilisation:

PS> $a = 1
PS> function foo {1}
PS> foo
1
PS> Import-Module .\psession.psm1
PS> gcm -Module psession


CommandType     Name                                                Definition
-----------     ----                                                ----------
Function        Clear-PSSession                                     ...



PS> Clear-PSSession
PS> foo
Le terme « foo » n'est pas reconnu comme nom d'applet de commande, fonction, fichier de script ou prog
 Vérifiez l'orthographe du nom, ou si un chemin d'accès existe, vérifiez que le chemin d'accès est cor
.
Au niveau de ligne : 1 Caractère : 4
+ foo <<<<
    + CategoryInfo          : ObjectNotFound: (foo:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException


PS> $a
PS>

samedi 10 mars 2012

Get-WMIHelp : un Get-Help pour WMI

salut, dans le post précédent, la fonction Get-WMIHelp nous permettait une aide afficher dans la console..dans ce post on va rendre cette aide plus conviviale.. voici la nouvelle fonction
function Get-WMIHelp {  
  <#
.SYNOPSIS

  Affiche des informations sur les classes et propriétés WMI

.DESCRIPTION

  Affiche des informations sur les classes et propriétés WMI

.PARAMETER InputObject

Spécifie un objets  représentant l'objet à récupérer. Entrez une variable contenant l'objets, ou tapez une commande ou une expression permettant d'obtenir cet objet. Vous pouvez également diriger un objet
  vers Get-WMIHelp. 
 
 .PARAMETER Class
 Spécifie le nom d'une classe WMI
 
.PARAMETER Path
 
 Spécifie le chemin d'accès de l'emplacement où le fichier de sortie éléments doit être copiés.

.EXAMPLE

PS C:\> Get-WmiObject -List win32_Bios | Get-WMIHelp

Cette commande obtient toutes les données disponibles sur les paramètres de la classe WMI 'win32_Bios'


.EXAMPLE

PS C:\> Get-WMIHelp -InputObject ([WMIClass]'win32_process')

.EXAMPLE

PS C:\> $WMI = Get-WmiObject -List win32_share
PS C:\> Get-WMIHelp -InputObject $WMI -Path Z:\shareinfo.htm

 La première commande obtient les méta données de la classe WMI 'win32_share', puis elle les stocke dans la variable $WMI.
 La deuxième commande utilise le paramètre InputObject pour passer l'objet  qui est stocké dans la variable $WMI à la fonction Get-WMIHelp. 
 Le paramètre 'Path' permet de spécifier un chemin pour stocké notre fichier "htm"


.EXAMPLE

PS C:\> Get-WMIHelp -Class "win32_OperatingSystem" -path c:\os.htm

 
.LINK
about_WMI_Cmdlets
Get-WmiObject

.INPUTS
System.Management.ManagementClass

.NOTES

   Author: Walid Toumi
   Blog: http://walidtoumi.blogspot.com
   Date: 2/28/2012
   Keywords: WMI - Get-WmiObject - Help
#>
  [CmdletBinding(DefaultParameterSetName='InputObject')]
  param(
   [Parameter(
     ValueFromPipeLine=$True,
     Mandatory=$True,
     Position=0,
     ParameterSetName='InputObject')]
   [ValidateNotNullOrEmpty()]
   [System.Management.ManagementClass]
   ${InputObject},
   [Parameter(Position=0,ParameterSetName='class')]
   ${Class},
   [System.String]
   ${Path})
 
  Try{ 
       switch ($PSCmdlet.ParameterSetName) {
        "Class" {
           $null=Get-WmiObject -Class $Class -ea 'stop'
           $IS_WMI_CLASS = $Class
         }
        "InputObject" {
              $IS_WMI_CLASS = $InputObject.__CLASS
         }
       }
       $WMIClass = Get-WmiObject -List $IS_WMI_CLASS -Amended
       $Desc = $WMIClass.Qualifiers['Description']
       $WMIProperties = $WMIClass.PSbase.Properties
       $Head=@"
     
"@
     $Body = @"
    

WMI CLASS

$($WMIClass.__Class)

WMI CLASS DESCRIPTION

$($Desc.Value)

PARAMETERS

"@ $WMIHelp = $WMIProperties | ForEach-Object { $desc = $($WMIClass.psbase.Properties["$($_.Name)"]).Qualifiers['Description'] New-Object PSObject -Property @{ Description = $desc.value Type = $_.Type Name = $_.Name } } <# end foreach #> | ConvertTo-Html -Property name,Type,description -Head $Head -Body $Body } catch { Write-Error $_ } finally { $WMIHelp > $env:TEMP\wmihelp.htm if($Path) { Try{ Move-Item $env:TEMP\wmihelp.htm $Path -ea 'stop' } catch { Write-Error $_ } } # end if else { Invoke-Item -Path $env:TEMP\wmihelp.htm } # end else } # end finally }

mardi 28 février 2012

invoke-item hklm:\softawre est-ce possible ?

Salut, parfois on est confronté devant des cas ou l'affichage des données dans le prompt n'est plus adéquate, pour cette raison l'equipe de developpeur de PS nous a fourni une cmdlet spécial du nom 'invoke-item' cette cmdlet, nous permet d'executer des actions sur les objets, tel que: * ouvrir le fichier 'a.txt' avec l'application qui lui est associée * ou bien "ouvrir le dossier 'c:\tree'" * ou bien "ouvrir tous les fichiers qui commencent par 't' et qui se termine par 'txt'" cette cmdlet est plus complète que la commande 'start' parcequ'elle permet l'utilisation des wildcards et permet de filtrer les élements selon certains critères. malheureusement cette cmdlet n'est pas suporter par le psprovider 'Registry' .... si on fait une chose du genre:
PS Walid2mi>> Invoke-Item HKCU:\Software
ça va nous retourner une erreur:
Invoke-Item : L'exécution du fournisseur s'est arrêtée, car le fournisseur ne prend pas en charge cette opération.
comme vous venez de constatez, le 'Registry' n'est pas le même environement que le 'FileSystem' une des solutions possibles est de créer notre propre fonction 'Invoke-Registry' qui va nous permettre d'afficher le registre sur la clef qu'on entrera comma paramètre...voici la fonction
function Invoke-Registry {

<#
.SYNOPSIS
   affiche le registre sur la clef spécifié
   
.DESCRIPTION
   affiche le registre sur la clef spécifié
 
.PARAMETER Path
Spécifie le chemin d'accès de la clef de registre sélectionné

.PARAMETER Maximize
maximize la fenêtre du registre
 
.EXAMPLE
   PS HKLM:\Softawre> invoke-Registry .
 
.EXAMPLE
   PS C:\> Invoke-Registry -Path 'Registry::\HKEY_Classes_Root\txtfile'
 
.EXAMPLE
   PS C:\> Invoke-Registry HKCU:\Environment -Max
 
.LINK
 invoke-item

.INPUTS
 none
vous ne pouvez pas diriger des objets pour cette fonction 

.OUTPUTS
 none 

.NOTES
   Author: Walid Toumi
   Blog: http://walidtoumi.blogspot.com
   Date: 2/28/2012
   Keywords: Registry - Invoke-Item
#>
    param(
      [Parameter(Mandatory=$true)]
      [ValidateScript({
             if(((item $_).PSProvider.Name -eq 'Registry') -or $_ -eq '.') 
             {$true} else {throw 'le paramètre "Path" doit correspondre à une clef de registre valide'}})]
        $Path,
      [Switch]$Maximize)
       try{
            $ErrorActionPreference = 1
            $key = (Get-Item $Path).Name
            $item = get-itemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit -Name lastkey
            $item.LastKey = "My Computer\$key"
            Set-ItemProperty -Path $item.PSPath -Name lastkey $item.LastKey
            if($Maximize) { invoke-expression "$env:comSpec /c start /max regedit.exe" } else { regedit.exe }
       } 
       catch {
            Write-Error $_.Exception.message
        }
       finally{
           $ErrorActionPreference = 2
       }
}
cette solution n'est peux être pas la plus optimale, mais elle fonctionne. si je trouve un peux de 'temps' et de 'paix'...ces jours-ci...je pourrais même envisagé de créer une fonction proxy du cmdlet 'invoke-item' qui utiliserait cette technique.. à bientôt alors...que la paix soit avec nous! ;)