Windows PowerShellRapports de progression

Don Jones

J'ai récemment écrit un script Windows PowerShell assez long et compliqué qui, à mesure de son exécution, ne renvoyait pas de réponse. Je l'avais écrit pour qu'il soit exécuté comme tâche planifiée, donc il n'avait pas réellement à produire une sortie très visible. Lorsque je l'ai exécuté pour son premier grand test, j'ai néanmoins commencé à me demander si je n'avais pas

écrit accidentellement une boucle infinie ou un autre problème de script.

Et comme le shell faisait clignoter de façon pathétique son petit curseur, je me suis demandé, « Que se passe-t-il ? L'ai-je raté » ? De toute évidence je manquais de confiance en moi car j'ai vite appuyé sur Ctrl+ C pour arrêter le script. Le temps d'ajouter un rapport de progression

Blablabla

La première chose j'ai voulu faire a été d'ajouter plusieurs messages d'état, pour me permettre de savoir exactement ce que le script faisait. Le shell vous permet de faire cela assez facilement avec l'applet de commande Write-Verbose. Allez-y, essayez-la dans le shell :

Write-Verbose "Test Message"

Si vous venez d'essayer, vous aurez remarqué que rien ne s'est passé. C'est parce que Write-Verbose envoie des objets dans le pipeline Verbose spécial, qui, par défaut, n'affiche pas sa sortie. Une variable shell intégrée, $VerbosePreference, contrôle ce pipeline. La valeur par défaut de cette variable est SilentlyContinue qui supprime la sortie détaillée. Configurez-la sur Continue, cependant, pour ouvrir le pipeline :

$VerbosePreference = "Continue"

Maintenant je peux ajouter plusieurs instructions Write-Verbose à mon script et obtenir un aperçu détaillé de ce qui se passe lorsqu'il s'exécute. Et voilà la beauté de cette technique : lorsque j'ai terminé le test et le dépannage, je peux éteindre tout ce blabla supplémentaire en configurant $Write-Verbose à nouveau sur SilentlyContinue au début de mon script.

Il n'est pas nécessaire de supprimer toutes les instructions Write-Verbose. En fait, puisqu'elles restent dans le script, chaque fois que j'ai besoin d'exécuter le script manuellement, je peux réactiver facilement le pipeline Verbose si nécessaire.

Mais j'ai besoin d'une réelle progression

Une fois que j'ai vérifié que le script n'était pas coincé dans une boucle infinie et qu'en fait, il fonctionnait parfaitement, j'ai désactivé le pipeline Verbose et je l'ai exécuté à nouveau, juste pour vérifier.

Maintenant j'ai un nouveau problème : même si je sais pertinemment que le script fonctionne bien, je ne peux pas supporter de regarder le curseur qui clignote. (J'ai des problèmes de concentration. J'ai cherché désespérément de la peinture de manière à pouvoir m'asseoir pour la regarder sécher au lieu de fixer le curseur).

Ce dont j'avais besoin, c'était d'une indication générale sur la progression du script et d'avoir une idée du moment auquel il serait terminé. En résumé, je cherchais une sorte de barre de progression.

Heureusement, Windows PowerShellTM intègre l'applet de commande Write-Progress. Cette applet de commande ne fournit pas une barre de progression graphique comme celle de Windows®, mais elle génère néanmoins une barre de progression utile, comme illustré à la figure 1. Elle ressemble un peu à la barre de progression de copie de fichier utilisée par la portion texte de l'installation dans Windows Server® 2003 ou même Windows XP.

Figure 1 Quelle est la progression de votre script ?

Figure 1** Quelle est la progression de votre script ? **(Cliquer sur l'image pour l'agrandir)

L'utilisation de Write-Progress exige quelques explications. En fait, je pense qu'il serait même préférable que je donne un exemple. Examinez ce script :

for ($a=1; $a -lt 100; $a++) {
  Write-Progress -Activity "Working..." `
   -PercentComplete $a -CurrentOperation
   "$a% complete" `
   -Status "Please wait."
  Start-Sleep 1
}

Il utilise Write-Progress pour afficher une barre de progression. J'ai utilisé Start-Sleep pour que le script s'interrompe une seconde chaque fois qu'il passe dans la boucle, afin qu'il exécute suffisamment lentement pour que nous puissions voir réellement la progression. Sans pause, la boucle compte de 0 à 100 si rapidement que la barre de progression clignote trop brièvement à l'écran.

Comme vous pouvez le voir, Activity (que j'ai défini sur Working) s'affiche en haut de la barre de progression. Status apparaît à droite juste en dessous et CurrentOperation s'affiche en bas. Le shell ne prend en charge qu'une seule barre de progression à la fois. Toute utilisation de Write-Progress crée une nouvelle barre de progression si aucune n'est affichée ou met à jour la barre en cours d'affichage.

Je n'ai pas encore indiqué ici à la barre de disparaître lorsque le script était terminé. Je peux le faire en ajoutant simplement ceci à la fin de mon script :

Write-Progress -Activity "Working..." `
 -Completed -Status "All done."

De manière générale, la barre de progression disparaît d'elle-même dès que votre script est terminé, mais si ce dernier a d'autres actions à accomplir, vous voudrez certainement masquer la barre de progression lorsque vous avez terminé. Le paramètre Completed supprime tout simplement la barre de l'écran.

Tic-tac, tic-tac, tic-tac

L'autre utilisation courante de Write-Progress est de créer un affichage des « secondes restantes » plutôt qu'une véritable barre de progression. Voici un exemple :

for ($a=100; $a -gt 1; $a--) {
  Write-Progress -Activity "Working..." `
   -SecondsRemaining $a -CurrentOperation
   "$a% complete" `
   -Status "Please wait."
  Start-Sleep 1
}

Tout ce que j'ai fait ici est de modifier la boucle pour compter de 100 à 1, et j'ai utilisé le paramètre SecondsRemaining de Write-Progress, au lieu de PercentComplete. Le résultat est illustré à la figure 2. Comme vous pouvez le voir, le compteur de progression a disparu, remplacé par une horloge de compte à rebours. Le shell convertit automatiquement le nombre total de secondes restantes en heures, minutes et secondes pour fournir une information plus facile à utiliser. En fait, le pourcentage terminé affiché ici compte à rebours à partir de 100, puisque je n'ai utilisé que le paramètre CurrentOperation. Il n'y a pas de véritable calcul de pourcentage terminé ; Windows PowerShell affiche seulement la valeur actuelle de $a suivie de la chaîne «%complete ».

Figure 2 Dans combien de temps votre script sera-t-il terminé ?

Figure 2** Dans combien de temps votre script sera-t-il terminé ? **(Cliquer sur l'image pour l'agrandir)

Des scripts qui communiquent

J'adore écrire des scripts qui vous informent de leur progression. Cela peut prendre la forme d'une sortie détaillée ou simplement d'une barre de progression. Cette information a pour but de compenser mon propre déficit d'attention ou de servir à quiconque devra exécuter mon script dans les mois à venir. Sans aucun doute, tout type d'affichage des informations d'état et de progression offre un réel avantage.

Applet de commande du mois Tee-Object

Ce mois-ci, je veux examiner l'une de mes applets de commande favorites pour le dépannage. Regardez ce code, par exemple :

Get-WMIObject Win32_Service | Where { $_.State -ne "Running"
-and $_.StartMode -eq "Automatic" } | ForEach-Object { $_.Start() }

En surface, on dirait qu'il démarre tous les services qui sont configurés pour démarrer automatiquement mais qui n'ont pas encore démarré pour une raison quelconque. En fait, cela ne fonctionne pas et trouver la raison de ce dysfonctionnement peut être difficile puisque vous ne pouvez pas regarder dans le milieu du pipeline. Pour regarder dans le pipeline, vous pouvez utiliser Tee-Object.

Tee-Object redirige les objets vers un fichier (ou dans une variable) et les transmet dans le pipeline. Par exemple :

Get-WMIObject Win32_Service | Tee-Object AllServices.csv | Where 
{ $_.State -ne "Running" -and $_.StartMode -eq "Automatic" } | 
Tee-Object FilteredServices.csv | ForEach-Object { $_.Start() }

Cette modification me permet de voir ce qui se passe après chaque commande pipeline, et je découvre rapidement que mon fichier FilteredServices.csv ne contient rien ! Pas étonnant que ce script ne fonctionnait pas ! Une recherche un peu plus approfondie me révèle la cause initiale du problème : StartMode est « Auto » et non « Automatic » et Tee-Object me permet de repérer exactement l'endroit où le problème est survenu.

Don Jonescontribue à l'élaboration de TechNet Magazine et est le coauteur de Windows PowerShell* : TFM* (SAPIEN Press, 2007). Il enseigne Windows PowerShell (voir www.ScriptingTraining.com) et peut être contacté par le biais du site Web ScriptingAnswers.com.

© 2008 Microsoft Corporation et CMP Media, LLC. Tous droits réservés. Toute reproduction, totale ou partielle, est interdite sans autorisation préalable.