Powershell en 10 min: aide, gestion d’erreurs et manipulation de fichiers (partie 4)

Cet article liste quelques fonctionnalités un peu plus avancées de Powershell pour traiter quelques cas d’implémentation courants.

Messages d’aide

Pour obtenir de l’aide sur une fonction, on peut utiliser l’instruction:

Get-Help <nom de la fonction>

Pour créer des messages d’aide lorsqu’on définit une fonction, on peut utiliser des commentaires déclarés avec une syntaxe particulière. Ces commentaires seront affichés suivant les paramètres utilisés à l’exécution de la fonction Get-Help.

Par exemple:
On peut indiquer dans le corps de la fonction les commentaires:

  • # .SYNOPSIS pour indiquer un texte décrivant brièvement la fonction
  • # .DESCRIPTION permet de décrire la fonction
  • # .PARAMETER donnera des indications sur les paramètres de la fonction
  • # .EXAMPLE indiquera un exemple

Il est possible d’indiquer d’autres informations:

  • # .INPUTS
  • # .OUTPUTS
  • # .NOTES
  • # .LINK

Dans le corps de la fonction, ces commentaires doivent être indiqués de cette façon:

function NomFonction($arg1, $arg2)
{
<#

.SYNOPSIS
Texte correspondant au synopsis 

.DESCRIPTION
Texte correspondant à la description 

.PARAMETER arg1
Aide concernant l'argument 1

.PARAMETER arg2
Aide concernant l'argument 1

.EXAMPLE
Texte correspondant à l'exemple 

.NOTES
Notes diverses 

.LINK
Liens éventuels

.INPUTS
Indication pour les données en entrée

.OUTPUTS
Indication pour les données en sortie

#>

  Write-Host Argument 1: $arg1
  Write-Host Argument 2: $arg2
}

Pour obtenir la documentation complète d’une fonction, il faut utiliser l’option -full:

Get-Help <nom de la fonction> -full

Dans le cas de l’exemple, pour afficher une documentation partielle, on peut exécuter:

PS C:\> Get-Help NomFonction

NAME
    NomFonction
    
SYNOPSIS
    Texte correspondant au synopsis
    
    
SYNTAX
    NomFonction [[-arg1] <Object>] [[-arg2] <Object>] [<CommonParameters>]
    
    
DESCRIPTION
    Texte correspondant à la description
    

RELATED LINKS
    Liens éventuels 

REMARKS
    To see the examples, type: "get-help NomFonction -examples".
    For more information, type: "get-help NomFonction -detailed".
    For technical information, type: "get-help NomFonction -full".
    For online help, type: "get-help NomFonction -online"

Dans le cas de la documentation complète, le résultat est:

PS C:\> Get-Help NomFonction -full

NAME
    NomFonction
    
SYNOPSIS
    Texte correspondant au synopsis
    
    
SYNTAX
    NomFonction [[-arg1] <Object>] [[-arg2] <Object>] [<CommonParameters>]
    
    
DESCRIPTION
    Texte correspondant à la description
    

PARAMETERS
    -arg1 <Object>
        Aide concernant l'argument 1
        
        Required?                    false
        Position?                    1
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -arg2 <Object>
        Aide concernant l'argument 1
        
        Required?                    false
        Position?                    2
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see 
        about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 
    
INPUTS
    Indication pour les données en entrée
    
    
OUTPUTS
    Indication pour les données en sortie
    
    
NOTES
    
    
        Notes diverses
    
    -------------------------- EXAMPLE 1 --------------------------
    
    PS C:\>Texte correspondant à l'exemple
    
    
    
    
    
    
    
RELATED LINKS
    Liens éventuels

Gestion des erreurs

Pour gérer n’importe quel type d’erreurs qui pourrait intervenir dans la fonction, il faut utiliser le mot clé trap. Par exemple:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
   }
   
   Write-Host Exécuté après "trap"
}

Dans ce cas, il n’y a pas d’erreurs donc le contenu de la partie trap ne sera pas exécutée:

PS C:\> NomFonction
Exécuté avant trap
Exécuté après trap

Dans le cas d’une erreur, le contenu de la partie trap est exécutée:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
   }
   # ATTENTION: l'exécution continue après le "trap"

   # L'erreur est une division par zéro
   $arg= 3/0

   Write-Host Exécuté après "trap"
}

Le résultat de l’exécution est:

PS C:\> NomFonction
Exécuté avant trap
Une erreur est survenue
Tentative de division par zéro.
At line:12 char:4
+    $arg= 3/0
+    ~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException
 
Exécuté après trap

break

Comme indiqué en commentaire de l’exemple précédent, l’exécution se poursuit après l’erreur. Pour stopper l’exécution à la ligne où l’erreur se produit, il faut ajouter break dans le bloc trap:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
      break
   }
   # ATTENTION: l'exécution continue après le "trap"

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

En exécutant, on obtient:

PS C:\> NomFonction
Exécuté avant trap
Une erreur est survenue
Tentative de division par zéro.
At line:12 char:4
+    $arg= 3/0
+    ~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RuntimeException

continue

A l’opposé de l’exemple précédent, si on veut que l’exécution se poursuive après la ligne où l’erreur s’est produite, il faut utiliser le mot clé continue. Avec continue, il n’y aura pas de message d’erreurs:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
      continue
   }
   # ATTENTION: l'exécution continue après le "trap"

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

En exécutant la fonction, on obtient:

PS C:\> NomFonction
Exécuté avant trap
Une erreur est survenue
Exécuté après trap

Pour afficher l’erreur avec un texte choisi, on peut utiliser $_:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host L`'erreur survenue est:
      Write-Host $_
      continue
   }
   # ATTENTION: l'exécution continue après le "trap"

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

Le résultat de l’exécution dans ce cas est:

PS C:\> NomFonction
Exécuté avant trap
L'erreur survenue est:
Tentative de division par zéro.
Exécuté après trap

Spécialiser l’erreur attrapée par “trap”

Pour spécialiser l’erreur attrapée par un bloc trap, il faut préciser le type d’exception comme le bloc try...catch en C#:

function NomFonction()
{
Write-Host Exécuté avant "trap"

   trap [System.DivideByZeroException]
   {
      Write-Host L`'erreur est:
      Write-Host $_
      continue
   }

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

Le résultat de l’exécution est similaire aux exemples précédents:

PS C:\> NomFonction
Exécuté avant trap
L'erreur est:
Tentative de division par zéro.
Exécuté après trap

En faisant cohabiter avec une autre bloc trap plus général:

function NomFonction()
{
Write-Host Exécuté avant "trap"

   trap [System.DivideByZeroException]
   {
      Write-Host L`'erreur est:
      Write-Host $_
      continue
   }
   trap 
   {
      Write-Host Autre erreur est:
      Write-Host $_
      continue
   }

   $unknownParameter = "chaine"
   $unknownParameter = 4 + $unknownParameter

  Write-Host Exécuté après "trap"
}

Pour la fonction précédente, l’erreur n’est pas une division par zéro donc c’est le 2e bloc trap qui sera exécuté:

PS C:\> NomFonction
Exécuté avant trap
Autre erreur est:
Cannot convert value "chaine" to type "System.Int32". Error: "Le format de la chaîne d'entrée est incorrect."
Exécuté après trap

Manipulation de fichiers

Pour lire le contenu d’un fichier:

Get-Content "<chemin du fichier>"

Assigner le contenu d’un fichier à une variable:

$val=Get-Content "<chemin du fichier>"

Dans le cas d’un fichier qui contient les lignes suivantes:

First line
Second line
Third line

Si on écrit la ligne suivante, le résultat est:

PS C:\> Write-Host $val
First line Second line Third line

Le résultat est un tableau contenant toutes les lignes. Chaque ligne est accessible en écrivant:

$val[0]

Dans le cas de l’exemple précédent:

Write-Host $val[0]
First line

Pour combiner les éléments du tableau dans une chaîne de caractères unique:

$separateur = [System.Environment]::NewLine
$result = [String]::Join($separateur, $val)

$result est donc une chaîne de caractères contenant tous les éléments du tableau.

Si on affiche son contenu, on obtient:

Write-Host $result
First line
Second line
Third line

Lister des fichiers dans un répertoire

Pour afficher des informations sur liste de fichiers dans un répertoire, on utilise Get-ChildItem. Dans le cas d’un seul fichier, on utilise Get-Item. On peut sélectionner les informations à afficher avec les paramètres suivants:

  • -Name: pour afficher le nom du fichier
  • -Recurse: pour parcourir un répertoire récursivement
  • -Path: pour afficher le chemin du fichier.

Par exemple pour afficher le chemin des fichiers se trouvant dans le répertoire courant (Get-Location renvoie le chemin du répertoire courant):

Get-ChildItem -Path (Get-Location).Path

Manipulations basiques sur des fichiers

Quelques cmdlets permettent d’effectuer des opérations basiques sur les fichiers:

  • Remove-Item: pour supprimer un fichier ou un répertoire
  • Rename-Item: pour renommer un fichier ou un répertoire
  • Move-Item: pour déplacer un fichier ou un répertoire

Pour créer un répertoire on peut écrire la commande suivante:

New-Item -Path "<chemin du répertoire>" -ItemType Directory

Générer un fichier

Pour écrire un fichier, on peut écrire:

Set-Content -Value <variable avec le contenu à écrire> -Path "<chemin du fichier à écrire>"

Si le fichier existe déjà, Set-Content écrase son contenu, il ne rajoute d’élément à un contenu déjà existant.

Pour ajouter un contenu quand le fichier existe déjà, il faut utiliser Add-Content.

Par exemple:

Add-Content -Value <nom d'une variable avec le contenu à écrire> -Path "<chemin du fichier>"

Si le fichier n’existe pas, il sera créé.

On peut aussi utiliser la commande Out-File.
Par exemple pour générer un fichier temporaire:

$tempFilePath = [System.IO.Path]::GetTempFileName()
$fileContent = "Content to write to the temp file"
$fileContent | Out-File $tempFilePath

Exporter dans un fichier CSV

On peut exporter un fichier CSV avec des instructions particulières, par exemple:

Get-Process | Export-Csv "<nom du fichier>"

Ajouter un header au fichier CSV au moment d’importer le fichier:

$header = "col1", "col2", "col3"
$process = Import-Csv "<nom du fichier à importer>" -Header $header

Fichier XML

De même pour créer un fichier XML, on peut écrire:

$courseTemplate = @"
<employees>
  <employee id="1">
    <name>Person 1</name>
    <age>23</age>
  </employee>
  <employee id="2">
    <name>Person 2</name>
    <age>56</age>
  </employee>
  <employee id="3">
    <name>Person 3</name>
    <age>21</age>
  </employee>
</employees>
"@

$courseTemplate | Out-File "<chemin du fichier>"

Si on affiche le contenu de la variable, on s’aperçoit qu’il s’agit d’un type plus complexe qu’une chaîne de caractères:
Par exemple:

PS C:\> Write-Host $xmlVar
#document

En omettant Write-Host, on peut afficher davantage d’éléments:
Par exemple:

PS C:\> $xmlVar
employees
---------
employees

et

PS C:\> $xmlVar.employees
employee                      
--------                      
{Person 1, Person 2, Person 3}

Pour charger un fichier XML
On peut écrire le code suivant:

$xmlVar = New-Object xml 
$xmlVar.Load("<chemin du fichier à lire>")

Travailler sur un nœud XML
On peut atteindre un nœud particulier d’un fichier XML facilement de la façon suivante:

$nodeVal = (@($xmlVar.employees.employee)[0]).Clone()

employees.employee correspond à un élément du fichier; (@($xmlVar.employees.employee) contiendra un tableau avec toutes les occurences de l’élément.

Ainsi, pour afficher le contenu de $nodeVal:
Par exemple:

PS C:\> $nodeVal
id name     age
-- ----     ---
1  Person 1 23 

Pour ajouter des éléments XML
A partir de la variable définit précédemment, on peut éditer des valeurs et ajouter un nouveau nœud en écrivant:

$newVal = $nodeVal.Clone()
$newVal.name = "Person 4"
$newVal.age = "3"
$xmlVar.employees.AppendChild($newVal) > $null

Si on ne rajoute pas > $null dans la ligne .AppendChild($newVal), l’ajout sera redirigé vers l’écran.

Si on affiche de nouveau, le contenu de $xmlVar.employees, on s’aperçoit qu’un nœud a été rajouté:
Par exemple:

PS C:\> $xmlVar.employees
employee                      
--------                      
{Person 1, Person 2, Person 3, Person 4}

Pour supprimer le template du fichier ou supprimer un nœud du fichier:

$xmlVar.courses.course | 
  Where-Object {$_.Name -eq "Person 1"} |
  ForEach-Object {[void]$_
    $xmlVar.courses.RemoveChild($_)
  }

Pour sauver le fichier XML

$xmlVar.Save("<chemin du fichier>")

Pour charger directement un fichier XML
On peut utiliser le mot clé [xml], par exemple:

[xml]$xmlVar = Get-Content "<chemin du fichier>"
Les autres articles de cette série

Partie 1: exécuter Powershell

Partie 2: les cmdlets

Partie 3: instructions dans des scripts Powershell

Partie 4: aide, gestion d’erreurs et manipulation de fichiers

Références

0 responses... add one

bonjour,

merci pour ton site qui est très complet, j’ai appris de nombreuses astuces !!

je cherche un livre ou bien un site qui puisse m’expliqué comment utilisé une DLL managé en C#
au travers de powershell. j’utilise une DLL du constructeur ftdichip.com lequel fournit les sources et la DLL
associé. J’arrive facilement à utiliser les méthodes, mais la DLL contient aussi des types personnalisés
et je n’arrive pas à les exploiter lorsque ceux ci sont utilisé en paramètre d’une méthode

exemple:
$FtdiDllPath = $PSScriptRoot + “\FTD2XX_NET_v1.1.0\FTD2XX_NET.dll”
$objectFTDI = [Reflection.Assembly]::LoadFile($FtdiDllPath)
$objectFTDI = New-Object FTD2XX_NET.FTDI
$objectFTDI.GetDeviceList(parameter = $objectFTDI.FT_DEVICE_INFO_NODE)

bonne journée

Bonjour,

Je pense que tu devrais utiliser une méthode avec Add-Type pour exécuter du code C# qui appellerait directement les fonctions dans les DLL FTDI.
Cette méthode est explicitée sur ces sites:
https://blog.stefan-gossner.com/2010/05/07/using-csharp-c-code-in-powershell-scripts/
https://gist.github.com/Sh1n0g1/8e98dcaf8df5f2951f1e52607a127191
https://blog.adamfurmanek.pl/2016/03/19/executing-c-code-using-powershell-script/
http://executeautomation.com/blog/calling-c-code-in-powershell-and-vice-versa/

Il faut faire attention aux dépendances des DLL. A l’exécution, il faut vérifier s’il n’y a pas des erreurs liées à des dépendances manquantes pour les rajouter le cas échéant avec l’argument ReferencedAssemblies.

Leave a Reply