| Jacques's profilejanelPhotosBlogLists | Help |
|
janelPhotographie, PowerShell, Vista, entre autres... January 21 Mes scripts PowerShell sont polyglottes, même en v1 !Suite à la publication de mon billet sur la « localisation des scripts » dans PowerShell, une discussion intéressante a démarré sur un forum de PowerShell-Scripting.com où j’avais également publié l’information. Sur ce forum, Laurent Dardenne a évoqué les techniques possibles pour faire de la localisation avec la v1 de PowerShell. Je vous rappelle que la technique exposée dans mon billet repose sur des fonctionnalités apparues avec la v2.
Malheureusement, j’ai trouvé les techniques décrites par Laurent plutôt complexes et pas forcément tout à fait adaptées à ce que j’ai besoin de faire quand je veux proposer les différents messages d’un script en plusieurs langues. J’ai donc pris le temps de créer une fonction, très simple donc à priori très facilement perfectible, pour couvrir les besoins les plus courants sans avoir à installer la v2 de PowerShell.
Tout d’abord, voici le corps de la fonction (je l’ai appelée import-culturaldata pour la distinguer de la commandelette disponible dans la v2, mais je ne suis pas sûr que ce nom soit très approprié – je suis preneur de toute suggestion) :
function import-culturaldata
{
param (
[string]$filename = [IO.Path]::GetFileNameWithoutExtension((split-path -leaf $myinvocation.MyCommand)),
[System.Globalization.CultureInfo]$UIculture = (get-UIculture)
)
if ($myInvocation.ScriptName) { $scriptname = $myInvocation.ScriptName }
else { $scriptname = $myInvocation.MyCommand.Definition }
$fullpath = $(split-path $ScriptName) + "\$UIculture\$filename" + ".psl1"
$ht = @{}
get-content $fullpath | where {($_.TrimStart()) -ne "" -and ($_.TrimStart())[0] -ne "#"} | foreach {
$key, $value = $_.Split("=", 2)
$ht.Add($key.Trim(), $value.TrimStart())
}
$ht
}
La fonction accepte deux paramètres :
- $filename – par défaut, le nom du script qui appelle la fonction (sans son extension)
- $UIculture – par défaut, la culture de la session en cours
La fichier lu doit avoir une extension .psl1 (pour PowerShell Localization, v1-compliant). J’ai tenu à me démarquer de l’extension choisie pour le même usage dans la v2 (.psd1) car le format d’un fichier .psd1 est potentiellement bien plus complexe que celui utilisé par ma fonction. Un fichier .psl1 doit contenir des entrées au format suivant :
<nom> = <valeur>
<nom> peut être n’importe quel texte pouvant être utilisé par PowerShell pour créer un nom de variable. Le caractère "=" est interdit.
<valeur> peut être n’importe quel texte, y compris contenant des "=". Je n’ai pas testé l’insertion de here-strings mais je doute que ça marche de manière totalement transparente. En revanche, on peut insérer des variables ({0}, {1}, …) qui pourront être substituées en cours de script avec l’opérateur -f. Je fournis une illustration de cet usage dans l’exemple ci-dessous.
Pour faciliter la lecture d’un fichier .psl1 on pourra insérer des lignes vierges, des espaces avant ou après <nom>, des espaces avant <valeur> et des lignes de commentaire commençant par "#" (précédé ou non par des espaces).
La fonction retourne un tableau associatif (hashtable en anglais) dont les paires clé/valeur sont créées à partir des entrées du fichier (clé = <nom>, valeur = <valeur>).
Prenons un exemple d’utilisation archi-simple. Pour cet exemple, imaginons que j’ai sauvegardé la fonction ci-dessus dans un fichier c:\outils\import-culturaldata.ps1. Pour tester la fonction, j’ai un répertoire c:\tests qui contient un fichier test-local.ps1 et deux sous-répertoires, c:\tests\fr-FR et c:\tests\en-US contenant chacun un fichier test-local.psl1.
Voyons d’abord le contenu des deux fichiers test-local.psl1 :
PS> get-content fr-FR\test-local.psl1
# culture "fr-FR"
Welcome = Bienvenue {0} au jeu "Devinez un jour" !
Prompt = Tapez un nom de jour
Success = Vous avez deviné ! Toutes nos félicitations !
PS>
PS> get-content en-US\test-local.psl1
# culture "en-US"
Welcome = Welcome {0} to the "Guess a day" game!
Prompt = Enter the name of any day
Success = You had it right! Congratulations!
Vous aurez reconnu les messages déjà utilisés dans mon billet précédent (à peu de chose près).
Voyons maintenant le script de test, test-local.ps1 :
PS> get-content test-local.ps1
param ([System.Globalization.CultureInfo]$myculture = (get-UIculture))
. c:\outils\import-culturaldata.ps1
$messages = import-culturaldata -uiculture $myculture
$messages.Welcome –f $env:username
$messages.Prompt
$messages.Success
Vous le voyez, j’ai été au plus simple.
Le script accepte comme paramètre optionnel une culture, qui par défaut sera celle déjà en place. Ca permettra de tester rapidement les différentes localisations disponibles.
Ensuite, le script récupère les données localisées dans une variable $messages – vous noterez la syntaxe de ma fonction qui retourne son résultat dans une variable, au lieu de prendre le nom de la variable en argument comme c’est le cas avec import-localizeddata.
Pour finir, le script affiche les trois messages l’un à la suite de l’autre. Le premier message, $messages.Welcome, contient une variable {0} à laquelle je substitue le nom de l’utilisateur pour personnaliser le message.
Voici le script en action :
PS> .\test-local en-US
Welcome janel to the "Guess a day" game!
Enter the name of any day
You had it right! Congratulations!
PS>
PS> .\test-local
Bienvenue janel au jeu "Devinez un jour" !
Tapez un nom de jour
Vous avez deviné ! Toutes nos félicitations !
Voilà. Au passage, vous aurez peut-être trouvé un peu curieux le test suivant dans le code de la fonction :
if ($myInvocation.ScriptName) { $scriptname = $myInvocation.ScriptName }
else { $scriptname = $myInvocation.MyCommand.Definition }
J’ai dû ajouter ce test pour permettre à la fonction de tourner à la fois sur PowerShell v1 et la v2 CTP3. En effet, il semble qu’il y ait un bug avec la CTP3 dont $myinvocation.ScriptName ne retourne rien dans ce contexte, alors qu’elle devrait retourner le nom complet du script qui a appelé la fonction. Le bug est maintenant documenté, on peut donc espérer qu’il sera corrigé avec la prochaine fournée. En attendant, cette "rustine" permettra de maintenir le code tel quel sans se préoccuper de la version installée sur les postes qui l’utiliseront.
Bien à vous.
Janel January 19 PowerShell, svchost.exe et les fonctions avancées (1ere partie)En cette fin de matinée mon PC semblait très occupé, au moins autant que moi. En jetant un œil à la liste des tâches en cours j’ai pu constater qu’il restait très peu de mémoire disponible. Après avoir fermé quelques applications inutilement restées ouvertes depuis vendredi dernier, j’ai vu qu’il restait encore plusieurs processus gourmands, et notamment deux svchost.exe consommant chacun plus de 50 Mo.
On sait que Windows utilise les processus svchost.exe pour héberger des services n’ayant pas leur propre exécutable. Chaque instance de svchost.exe peut héberger plusieurs services, parfois plusieurs dizaines. On sait moins comment on peut obtenir la liste des services hébergés par un processus svchost.exe donné. Il existe plusieurs méthodes, je m’attarderai ici sur l’une d’entre elles qui fait appel à Windows PowerShell.
Lorsqu’il s’agit de manipuler des processus et des services dans PowerShell, on pense immédiatement à deux commandelettes fournies en standard :
PS> get-process
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 27 1 380 376 11 1952 AEADISRV 38 2 736 432 21 1968 agrsmsvc 183 3 2608 580 41 1024 Ati2evxx [...]
PS> get-service
Status Name DisplayName ------ ---- ----------- Stopped AddFiltr AddFiltr Stopped Adobe LM Service Adobe LM Service Running AeLookupSvc Application Experience [...]
Comme on pourrait s’y attendre, on peut facilement obtenir la liste des processus nommés svchost.exe :
PS> get-process svchost
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 331 5 4020 3856 45 816 svchost 732 11 6440 4872 56 884 svchost 469 20 106252 13912 206 932 svchost [...]
On peut même très facilement isoler les processus dont la mémoire de travail est supérieure à 50 Mo :
PS> get-process svchost | where {$_.workingset -gt 50MB}
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 1026 22 94800 79816 198 1068 svchost 2242 290 134076 98224 323 1080 svchost
Mais get-process ne fournit pas de méthode pour aller voir quels services se cachent dans ces processus. On pourrait à la rigueur s’en faire une idée en parcourant les modules chargés par chacun de ces processus, mais la liste serait assez rébarbative à parcourir et à recouper avec de véritables services Windows.
On aurait alors envie de se tourner vers get-service. Pendant un instant j’ai imaginé que les objets retournés par get-service avaient un numéro de tâche qui permettrait de les rattacher à un processus. Mais non. Ou alors, je n’ai pas trouvé.
J’ai alors pensé à un troisième larron : la classe WMI Win32_Service. PowerShell permet d’accéder très facilement aux classes WMI :
PS> get-wmiobject win32_service | get-member -membertype properties
TypeName: System.Management.ManagementObject#root\cimv2\Win32_Service
Name MemberType Definition ---- ---------- ---------- AcceptPause Property System.Boolean AcceptPause {get;set;} AcceptStop Property System.Boolean AcceptStop {get;set;} Caption Property System.String Caption {get;set;} CheckPoint Property System.UInt32 CheckPoint {get;set;} CreationClassName Property System.String CreationClassName {get;set;} Description Property System.String Description {get;set;} DesktopInteract Property System.Boolean DesktopInteract {get;set;} DisplayName Property System.String DisplayName {get;set;} ErrorControl Property System.String ErrorControl {get;set;} ExitCode Property System.UInt32 ExitCode {get;set;} InstallDate Property System.String InstallDate {get;set;} Name Property System.String Name {get;set;} PathName Property System.String PathName {get;set;} ProcessId Property System.UInt32 ProcessId {get;set;} ServiceSpecificExitCode Property System.UInt32 ServiceSpecificExitCode {get;set;} ServiceType Property System.String ServiceType {get;set;} Started Property System.Boolean Started {get;set;} StartMode Property System.String StartMode {get;set;} StartName Property System.String StartName {get;set;} State Property System.String State {get;set;} Status Property System.String Status {get;set;} SystemCreationClassName Property System.String SystemCreationClassName {get;set;} SystemName Property System.String SystemName {get;set;} TagId Property System.UInt32 TagId {get;set;} WaitHint Property System.UInt32 WaitHint {get;set;} [...]
Bingo ! La propriété ProcessId (surlignée ci-dessus) semble tout à fait correspondre à ce que je cherche, à savoir faire une jointure entre la liste des processus svchost.exe et la liste des services. Essayons :
PS> get-process svchost | where {$_.workingset -gt 50MB} | foreach { >> $id = $_.id; gwmi win32_service | where {$_.processi d -eq $id}} | >> ft proc*,name,displayname,stat* -auto >>
ProcessId name displayname state status --------- ---- ----------- ----- ------ 1068 AudioEndpointBuilder Windows Audio Endpoint Builder Running OK 1068 CscService Offline Files Running OK 1068 EMDMgmt ReadyBoost Running OK [...]
Et voilà.
NB. Je suis obligé de tronquer les commandes sur plusieurs lignes pour que l'affichage soit lisible dans mon blog. En réalité, je tape les commandes les unes à la suite des autres sur un même ligne.
Pour celles et ceux qui s’interrogent: oui, on pouvait s’épargner la création d’une variable intermédiaire ($id) mais ça n’apportait aucune amélioration dans les performances de la requête, ça rendait la ligne un peu plus complexe à déchiffrer, et au final ça m’aurait éloigné de l’objet de cette discussion. J
Quant à mon problème de mémoire vive utilisée par svchost.exe, cet exercice m’a surtout permis de constater qu’il y avait beaucoup de monde pour utiliser cette mémoire, ce qui n’est pas forcément un problème en soi. Il y aurait eu deux ou trois services hébergés par chacun de ces processus, le problème aurait été autrement plus criant. Là, je me suis contenté de parcourir la liste et d’arrêter les services qui ne me paraissaient pas indispensables.
Il ne me restait plus qu’à pousser cette ligne de commandes dans un script, de manière à m’éviter de devoir rechercher l’information à chaque fois qu’il m’arrive de vouloir obtenir l’information. Dans sa version la plus simple, le fichier ressemblait alors à ceci :
PS> get-content get-hostedservice.ps1 # get-hostedservice # # Returns a list of services hosted by a process. # # Syntax: get-hostedservice -id ProcessId
param ([int]$Id)
get-wmiobject Win32_Service | where {$_.ProcessId -eq $Id}
Et voici un exemple de mise en oeuvre :
PS> gps svchost | where {$_.workingset -gt 50MB} | foreach {get-hostedservice $_.id} | >> ft proc*,name,displayname,stat* -auto >>
ProcessId name displayname State Status --------- ---- ----------- ----- ------ 1068 AudioEndpointBuilder Windows Audio Endpoint Builder Running OK 1068 CscService Offline Files Running OK 1068 EMDMgmt ReadyBoost Running OK [...]
La ligne à taper est plus simple que la précédente, mais pas tant que ça. Encore faut-il se rappeler que le script accepte l’Id d’un processus comme paramètre. Et j’aurais aimé pouvoir simplement récupérer les objets du pipeline dans mon script, sans avoir à les traiter un par un dans ma ligne de commandes avec une boucle foreach.
C’est là que la v2 de PowerShell apporte quelques grosses améliorations qui vont nous simplifier encore plus la tâche. Mais nous verrons tout cela dans la 2e partie, à venir dans quelques jours. D’ici là, soyez sages ! J
Janel January 16 Mes scripts PowerShell sont polyglottesAvec la v2 CTP3 de PowerShell arrivent une foultitude de nouvelles fonctionnalités, toutes plus sympathiques les unes que les autres. On parle beaucoup de la gestion de sessions à distance et de l’exécution asynchrone de tâches (la seconde dépendant de la première). Personnellement, une autre fonctionnalité a attiré mon attention ces jours-ci et m’a paru digne d’intérêt pour toute personne qui aura besoin de diffuser son script à des populations ne comprenant pas forcément toutes la même langue.
Il s’agit de la possibilité de personnaliser un script de manière à ce que les messages qu’il affiche soient automatiquement choisis en fonction de la langue de l’utilisateur. En fait, on peut même adapter d’autres variables que les messages, mais dans la pratique ce sont essentiellement les messages destinés à l’utilisateur qu’on voudra « localiser » (anglicisme fréquemment utilisé pour décrire les différents travaux d’adaptation d’un logiciel aux particularités linguistiques et autres d’un pays). Cette possibilité, dans PowerShell, s’appelle « script internationalization ». Vous pourrez en trouver une description relativement sommaire dans les « release notes » de la v2 CTP3.
La fonctionnalité de « script internationalization » repose sur une autre fonctionnalité, la gestion de « DATA sections ». Il s’agit de blocs de données isolés du code pour faciliter la lecture du script et sa maintenance éventuelle. Un bloc de donnée est déclaré avec le mot-clé spécial DATA, suivi dans sa forme la plus simple d’un nom de variable (sans le signe $) et du contenu de cette variable. La syntaxe complète et plus d’informations sont accessibles depuis PowerShell en tapant :
help about_data_sections
Mais passons directement à l’utilisation concrète de blocs de données dans un script que nous voudrons offrir en plusieurs langues.
Soit le script suivant, qu’on appellera GuessADay.ps1 :
# culture="fr-FR" par défaut
Data day {
ConvertFrom-StringData @'
d1 = lundi
d2 = mardi
d3 = mercredi
d4 = jeudi
d5 = vendredi
d6 = samedi
d7 = dimanche
'@
}
Data messages {
ConvertFrom-StringData @'
Welcome = Bienvenue au jeu "Devinez un jour" !
Prompt = Tapez un nom de jour
Success = Vous avez deviné ! Toutes nos félicitations !
'@
}
Data errors {
ConvertFrom-StringData @"
NotExist = {0} n'existe pas. La bonne réponse était {1}.
WrongDay = Erreur! La bonne réponse était {0}.
"@
}
# importer les données traduites :
Import-LocalizedData -bindingvariable day -filename day -ea silentlycontinue
Import-LocalizedData -bindingvariable messages -filename messages -ea silentlycontinue
Import-LocalizedData -bindingvariable errors -filename errors -ea silentlycontinue
# Construire un tableau avec les noms de jours :
$days = $day.d1, $day.d2, $day.d3, $day.d4, $day.d5, $day.d6, $day.d7
# Choisir un jour au hasard :
$computerday = get-random $days
# accueillir le joueur et lui proposer de saisir un nom de jour :
$messages.Welcome
$userday = read-host $messages.Prompt
# vérifier le nom saisi et afficher un message en conséquence :
switch ($userday)
{
$computerday { $messages.Success; break }
{$days -notcontains $_} { $errors.NotExist -f $userday,$computerday; break }
default { $errors.WrongDay -f $computerday }
}
Il s’agit d’un petit jeu extrêmement simple et totalement stupide. L’ordinateur choisit un jour de la semaine au hasard et demande à l’utilisateur de le deviner.
L’intérêt de ce script est de montrer comment on peut charger des messages adaptés à la langue de l’utilisateur, telle qu’elle est définie dans la session PowerShell en cours.
En l’occurrence, les messages du script GuessADay.ps1 ont été regroupés dans trois variables :
Data day {
ConvertFrom-StringData @'
d1 = lundi
d2 = mardi
d3 = mercredi
d4 = jeudi
d5 = vendredi
d6 = samedi
d7 = dimanche
'@
}
La première variable, $day, est un tableau contenant les noms des jours de la semaine. $day.d1 = "lundi", $day.d2 = "mardi", etc. Vous noterez la syntaxe du bloc DATA, un peu particulière. Je vous rappelle que vous trouverez plus d’explications dans l’aide en ligne (cf. plus haut dans ce billet).
Data messages {
ConvertFrom-StringData @'
Welcome = Bienvenue au jeu "Devinez un jour" !
Prompt = Tapez un nom de jour
Success = Vous avez deviné ! Toutes nos félicitations !
'@
}
La deuxième variable, $messages, contient les messages utilisés au cours du jeu. $messages.Welcome est un message de bienvenue, $messages.Prompt est le message utilisé pour inviter le joueur à saisir un nom de jour, et $messages.Success est le message affiché en cas de succès.
Data errors {
ConvertFrom-StringData @'
NotExist = {0} n'existe pas. La bonne réponse était {1}.
WrongDay = Erreur! La bonne réponse était {0}.
'@
}
Enfin, la troisième variable, $errors, contient les messages d’erreur. Je les ai isolés des messages standards, notamment pour mettre en évidence une technique particulière qui est souvent utile pour les messages d’erreurs, à savoir l’inclusion de paramètres dans le message. Ici, $errors.NotExist est utilisé si le jour saisi ne correspond à aucun nom de jour connu, et $errors.WrongDay est utilisé si le jour saisi n’est pas celui que l’ordinateur avait choisi.
L’inclusion de paramètres utilise la technique de formatage de chaînes de caractères avec l’opérateur -f. Chaque paramètre est identifié dans le texte par son numéro d’ordre inséré entre crochets : {0} pour le premier paramètre, {1} pour le deuxième, et ainsi de suite. Au moment de l’affichage du message, il suffit de passer les paramètres à la suite du message et de l’opérateur -f :
$errors.NotExist -f $userday,$computerday
La ligne ci-dessus va afficher le message $errors.NotExist, en remplaçant {0} par $userday et {1} par $computerday. Et voilà. J
Tout cela est bien joli, mais tous les message de ce script sont en français. Alors, où est la traduction automatique tant vantée ?
Patientez, j’y viens ! En fait, il va nous falloir créer quelques fichiers supplémentaires, et même un (ou plusieurs) répertoire(s) supplémentaire(s). Prenons un exemple simple où nous voulons diffuser notre script à des américains. S’ils l’exécutent tel quel, ils risquent fort de ne pas comprendre grand-chose. On va donc créer des fichiers qui contiendront des traductions en anglais des différents messages du jeu.
Pour cela, on va d’abord créer un répertoire \en-US dans le répertoire où le script est stocké. Pourquoi « en-US » ? Parce que c’est le nom de la culture (l’ensemble des paramètres régionaux) retournée par un poste configuré pour un américain. On peut voir le nom de la culture utilisée pour l’interface de l’utilisateur en cours en tapant la commandelette suivante :
PS> get-UICulture
LCID Name DisplayName
---- ---- -----------
1036 fr-FR Français (France)
L’exemple ci-dessus correspond à la configuration classique d’un utilisateur français. Chez un américain, la même commandelette devrait retourner un résultat différent :
PS> get-UICulture
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
PowerShell va se servir du nom de la culture (“fr-FR” pour un français, "en-US") pour déterminer dans quel répertoire aller chercher les données spécifiques à cette culture. Cette opération est réalisée par la commandelette import-localizeddata :
Import-LocalizedData -bindingvariable day -filename day -ea silentlycontinue
Import-LocalizedData -bindingvariable messages -filename messages -ea silentlycontinue
Import-LocalizedData -bindingvariable errors -filename errors -ea silentlycontinue
Ici, le script réalise trois opérations d’importation distinctes, une par variable ($day, $messages et $errors). Chaque variable est stockée dans un fichier différent dont le nom est précisé après le paramètre –filename. L’extension du fichier n’est pas précisée, mais il s’agira forcément d’un fichier utilisant l’extension .psd1. On aura donc trois fichiers dans le répertoire \en-US :
PS> dir en-US
Directory: C:\Users\janel\documents\tests\GuessADay\en-US
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 16/01/2009 16:44 184 day.psd1
-a--- 16/01/2009 17:13 176 errors.psd1
-a--- 16/01/2009 16:49 193 messages.psd1
Si le script ne contenait qu’un seul bloc de données, on aurait eu un seul fichier de traduction à gérer. Dans ce cas, on pouvait ne pas préciser le nom de fichier et il suffisait de stocker les messages traduits dans un fichier .psd1 reprenant le nom du script (guessaday.psd1).
Mais voyons maintenant à quoi ressemblent les fichiers contenant les versions américaines de nos messages :
PS> dir en-US | foreach {"`n$_ :`n"; get-content $_.pspath}
day.psd1 :
# culture="en-US"
ConvertFrom-StringData @'
d1 = Monday
d2 = Tuesday
d3 = Wednesday
d4 = Thursday
d5 = Friday
d6 = Saturday
d7 = Sunday
'@
errors.psd1 :
# Culture="en-US"
ConvertFrom-StringData @'
NotExist = There is no such day as {0}. The correct answer was {1}.
WrongDay = Wrong! The correct answer was {0}.
'@
messages.psd1 :
# Culture "en-US"
ConvertFrom-StringData @'
Welcome = Welcome to the "Guess a day" game!
Prompt = Enter the name of any day
Success = You had it right! Congratulations!
'@
Dans chaque fichier on retrouve la même structure que dans le bloc de données correspondant, à ceci près qu’on ne reprend pas la déclaration "Data …". En fait, le fichier toute entier est lui-même considéré comme un bloc de données, et c’est l’opération d’importation qui s’occupe de faire le lien avec la variable correspondante.
Notez que la ligne de commentaire précisant la culture du fichier est tout à fait facultative. Je l’ai juste mise en tête de chaque fichier à titre d’information.
Une fois que tout ça est en place, il ne reste plus qu’à diffuser le script et ses fichiers de traduction (en faisant attention de toujours respecter l’arborescence avec les noms de culture).
Sur un poste français, voici ce que pourra donner une phase de jeu :
PS> .\guessaday
Bienvenue au jeu "Devinez un jour" !
Tapez un nom de jour: lundi
Erreur! La bonne réponse était dimanche.
Et le même script, sur un poste américain :
PS> .\guessaday
Welcome to the "Guess a day" game!
Enter the name of any day: Tuesday
You had it right! Congratulations!
Voilà. A vous de trouver un usage un tantinet plus utile. Evidemment, vous pouvez ajouter autant de répertoires que vous aurez de traductions à offrir, en faisant toujours attention de bien nommer les répertoires du nom de la culture à laquelle ils correspondent. Et n’oubliez pas que les messages utilisés dans le script seront ceux utilisés par défaut si aucun répertoire n’existe pour la langue de l’utilisateur. Dans mon exemple j’ai utilisé le français par défaut, mais il est possible que vous ayez à utiliser l’anglais par défaut et à fournir le français en traduction dans un répertoire fr-FR.
A vous de jouer !
Janel January 01 Dix lignes de Bling?En ce tout premier jour de l’année 2009, un nouveau script PowerShell fort intéressant a été posté par Clint Huffman sur Codeplex :
Bling.ps1 permet de créer un graphique à partir d’un log créé par Perfmon, l’outil de suivi de performances de Windows. Les logs sont acceptés au format BLG ou CSV. Chaque compteur de performance présent dans le log est représenté par une ligne sur le graphique.
La page ci-dessus fournit toutes les indications pour installer et utiliser le script. Je n’ai pas encore eu le temps de le tester, mais je ne manquerai pas de vous faire part de mes remarques le cas échéant. N’hésitez pas à laisser les vôtres en commentaire !
Même si dans sa v1 le script a quelques limitations, il s’agit à l’évidence d’une belle réalisation qui pourra inspirer de nombreux scripteurs.
Profitez bien, et comme dirait l’autre : allez 2009 !
Janel December 06 La fin de l'impérialisme a sonné pour les CSVQuelle bonne nouvelle ! Hier j’ai reçu une série de mails de Microsoft Connect m’indiquant que mon Feedback #152145 avait été rouvert et résolu.
Si cela ne vous parle pas, ne raccrochez pas tout de suite. Laissez-moi vous expliquer les choses dans l’ordre :
Microsoft Connect est le site web que Microsoft utilise pour échanger avec les utilisateurs de ses produits en cours de développement. On y accède à l’adresse https://connect.microsoft.com. A ma connaissance, il faut avoir été invité à participer à un programme de test pour pouvoir se connecter, mais il est possible que certains programmes soient ouverts à tout le monde.
Une des méthodes d’échange proposées sur ce site est la soumission de Feedback par les utilisateurs d’un produit. Ce Feedback peut être la soumission d’un bug, ou une suggestion pour l’ajout d’une fonctionnalité.
C’est ce second type de Feedback que j’avais utilisé en juin 2006 pour suggérer à l’équipe Windows PowerShell d’ajouter aux commandelettes import-csv et export-csv la possibilité d’accepter un autre séparateur que la virgule. En effet, ce séparateur n’est pas utilisé par tous les pays dans le monde. Par exemple, celui que nous utilisons en France est le point-virgule. Du coup, pour tout utilisateur ne disposant pas de réglages compatibles avec ceux des USA, les fichiers CSV créés par PowerShell ne pouvaient pas être lus directement dans Excel, et inversement les fichiers CSV générés par Excel ne pouvaient pas être importés directement dans PowerShell.
Le 30 juin 2006 j’ai donc soumis un Feedback, portant le numéro 152145, qui suggérait l’addition de deux paramètres :
-delimiter : ce paramètre permettrait à l’utilisateur de spécifier un caractère particulier à utiliser comme délimiteur,
-useRegionalSettings : ce paramètre, de type Switch, permettrait à l’utilisateur de forcer PowerShell à utiliser le séparateur de liste défini dans les paramètres régionaux de la session en cours.
A l’époque, ce Feedback a été fermé après une rapide évaluation et une réponse polie. Par la suite, le Feedback a été évalué comme « Very Important » par 17 utilisateurs (ce système de vote est une des forces de Connect, qui permet à Microsoft d’apprécier l’importance des Feedbacks remontés et de définir ses priorités en conséquence). Entre temps, le développement de la version 2 de PowerShell a démarré, et après moultes péripéties, j’ai donc reçu hier des mails m’indiquant successivement que mon Feedback était rouvert, qu’il était résolu et de nouveau fermé.
Ayant l’honneur et la chance d’être en possession d’une version récente de PowerShell v2 (à usage interne uniquement) je me suis précipité dessus pour vérifier, et ô joie ! Les commandelettes *-csv ont bien deux formes syntaxiques : une avec un paramètre optionnel –delimiter, l’autre avec un paramètre optionnel –useCulture (de type Switch). Vous noterez au passage la transformation de –useRegionalSettings en –useCulture, totalement appropriée puisque dans l’environnement .NET on parle de Culture pour désigner ce qui sous Windows est appelé Regional Settings.
D’ailleurs, le fichier d’aide des commandelettes donne un moyen simple pour savoir quel est le séparateur de liste configuré pour votre session :
PS> (get-culture).TextInfo.ListSeparator
Je me permets également de signaler la présence de deux autres commandelettes, qui me semble-t-il n’étaient pas présentes dans les versions précédentes de PowerShell : convertFrom-csv et convertTo-csv. Leur usage est similaire à import-csv et export-csv à ceci près qu’elles ne passent pas par un fichier.
J’ignore si la dernière CTP disponible incluait déjà ces modifications. N’ayant pas eu beaucoup l’occasion de me servir de PowerShell jusqu’à ces deux dernières semaines, j’avais un peu perdu le fil des nouveautés. N’hésitez pas à regarder et à laisser l’information en commentaire sur ce billet. Si la CTP n’a pas encore les paramètres décrits ci-dessus, patience… Vous devriez les voir apparaître dans la prochaine version.
A bientôt ?
Janel December 04 Nouveau blog de Microsoft sur PowerShell, en français!Hé oui, c’est bien moi ! Je suis toujours là, même si je n’ai rien publié sur ce blog depuis janvier. En fait, je ne compte pas spécialement reprendre une activité régulière, au contraire. Le but de ce billet est, en quelque sorte, de passer le relais en vous faisant part de la naissance du blog officiel de Microsoft sur Windows PowerShell en français :
Bon, l’URL ci-dessus ne semble pas spécialement francophone. Pourtant, depuis mon navigateur elle m’amène bien au blog francophone de Microsoft. Il est possible que cette URL évolue à l’avenir. Surveillez régulièrement si vous êtes intéressé(e).
Quant à moi, je me retire sur la pointe des pieds. Entre www.powershell-scripting.com, site indépendant et toujours aussi actif qui va bientôt atteindre les 150 000 visites, et le blog de Microsoft ci-dessus, vous avez quelques sources d’information régulières et sérieuses. Il m’arrivera peut-être de continuer à poster ici ou là, mais certainement pas aussi régulièrement que j’ai pu le faire en 2006/2007.
Bien à vous,
Janel January 29 Entraînez-vous pour les Scripting Games 2008!L’hiver est bien là, malgré la clémence du temps - en tout cas sur la région parisienne, et avec lui arrivent les Scripting Games organisés par Microsoft. La troisième saison se déroulera du 15 février au 3 mars 2008. Toutes les informations sont disponibles ici (en anglais) :
En explorant le site, j’ai vu qu’on pouvait consulter la liste des meilleurs performeurs toutes saisons confondues. J’ai parcouru la liste dans l’espoir d’y trouver des noms familiers, et voici par ordre décroissant les noms francophones figurant dans ce classement :
18e : Jean – JMST (Belgique) avec un score total de 295 points.
34e ex-æquo : Gilles Laurent (France) et Marco Shaw (Canada) avec un score total de 200 points.
Mes sincères félicitations à Jean, Gilles et Marco pour ce résultat extrêmement brillant qui témoigne d’une vraie passion et d’une connaissance pointue de leur langage favori (que ce soit VBScript, JScript ou PowerShell). J’en veux pour preuve la présence de MOW, une référence en la matière, au même niveau que Gilles et Marco avec un score total de 200 points.
Qui plus est, Jean et Marco font partie de la petite élite qui a réalisé un sans-faute en 2007. Chapeau bas messieurs.
Alors, ferez-vous partie de cette liste en 2008 ?
A vos claviers !
Janel Présentation sur Microsoft IT aux TechDaysHello,
Après plusieurs semaines de silence radio, me voici de retour. Dans les prochains jours j’essaierai de prendre le temps de terminer et de publier les quelques billets que j’ai en chantier depuis longtemps sur PowerShell, mais aujourd’hui je reprends la plume pour vous parler d’un tout autre sujet (enfin, pas si éloigné que ça quand même).
Vous ne le savez peut-être pas, mais les 11, 12 et 13 février 2008 (soit dans moins de deux semaines maintenant) Microsoft organise au Palais des Congrès de Paris les TechDays 2008. Cet évènement, gratuit, proposera environ 300 sessions (présentations et ateliers) sur la mise en œuvre des produits et solutions de Microsoft autour des grands thèmes de l’informatique d’aujourd’hui : sécurité, gestion des données, mobilité, productivité, design, décisionnel, etc. Au total 25 parcours permettent à chacun de se construire son propre agenda sur les 3 jours.
En ce qui me concerne, j’aurai la joie et le privilège de présenter une session le mercredi 13 février, de 16h à 17h :
Il s’agit d’un partage d’expérience sur notre démarche interne d’adoption des règles et des bonnes pratiques d’ITIL et de MOF. La présentation s’articulera principalement autour de l’implémentation de notre Catalogue de Services, mais j’évoquerai également la démarche de Gouvernance entreprise par l’équipe dans laquelle je travaille, ses enjeux et ses difficultés.
Pour votre information : à l’heure où j’écris ces lignes il y a 80 inscrits pour cette session, pour une capacité totale de 120 places dans la salle. Le nombre d’inscrits n’est qu’une indication basée sur les personnes qui utilisent le site web des TechDays pour planifier leurs sessions. Il n’y a aucune obligation liée à ces inscriptions.
Un dernier point : si jamais vous regrettez de ne pas pouvoir venir aux TechDays, ou si vous pensez venir mais savez déjà que vous ne pourrez pas assister à toutes les sessions que vous souhaiteriez voir, sachez que toutes les sessions seront enregistrées et disponibles en webcast dans les semaines qui suivront l’évènement. Je posterai le lien ici, mais vous pouvez également vous inscrire pour être sûr(e) de recevoir l’information.
A bientôt !
Janel December 07 Faire d'une liste plusieurs variables en un seul coupDans mon billet sur l’import et l’export des credentials, j’ai utilisé une syntaxe qui a appelé un commentaire de la part d’Arnaud :
$username,$password = type $filename
Cette ligne suppose que le contenu de $filename va être retourné sous forme d’une liste (System.Array). Le premier élément de la liste sera affecté à la variable $username et l’élément suivant sera affecté à la variable $password.
Le fichier $filename a été créé par le script export-credential.ps1. Regardons de plus près un fichier $filename créé sur mon poste :
PS> type cred.txt
\janel
01000000d06c9ddf0115d1118c7a00c04fc297eb010000000ea455d65181ad438e3639faf77e7ebc0000000002000000000003660000a8000000100
0000009d9b7e4e3f7bcde09d555a4e21d0b670000000004800000a00000001000af004e1681e8ff0a56ccb867796cd63d6db810000000c5a30b7222
450fde060021fa5b4abfa0240000003fa185001ed0132aaf91ecd50e0e49ae4f566b82
Le fichier contient deux lignes : la première représente bien le nom d’utilisateur (\janel) et la seconde est une longue suite de caractères alphanumériques. Il s’agit de la version cryptée du mot de passe. Le fait que la deuxième ligne s’étale sur plusieurs lignes à l’affichage ne veut pas dire qu’il s’agit de plusieurs lignes au sens où PowerShell l’entend dans le contexte d’un fichier texte : une ligne est simplement une suite de caractères terminée par un retour chariot ou par une fin de fichier, quelle que soit sa longueur.
Quand on lit un fichier avec la commande type (alias de la commandelette get-content), celle-ci retourne une liste dont les éléments sont les lignes du fichier. On a donc bien ici deux éléments correspondant aux deux lignes du fichier.
Mais, vous demandez-vous, que se passerait-il si le fichier contenait plus de lignes ? Ah, la bonne question que voilà ! Regardons avec un autre exemple :
PS> $pouce,$index = 1..5
PS> $pouce
1
PS> $index
2
3
4
5
Aïe ! La variable $pouce a bien reçu la valeur 1, premier élément de la liste créée par 1..5. Mais $index a récupéré tous les éléments suivants, de 2 à 5. Si j’avais voulu ne lui affecter que la valeur 2, il aurait fallu soit préciser que je ne voulais que les deux premiers éléments de la liste, soit ajouter une troisième variable pour récupérer les autres valeurs :
PS> $pouce,$index = (1..5)[0..1]
PS> $pouce
1
PS> $index
2
PS> $pouce,$index,$autres = 1..5
PS> $pouce
1
PS> $index
2
PS> $autres
3
4
5
Voilà. A vous de continuer d’explorer ce que vous pouvez faire avec cette syntaxe.
Janel Un avant-goût du prochain livre sur PowerShell en françaisOui, ça y est, les livres sur Windows PowerShell arrivent en français comme s’il en pleuvait ! Enfin, presque. Pour l’instant il n’y en a qu’un de publié, voir pour cela un de mes billets récents sur ce même blog.
Cela dit, voici enfin un avant-goût sur le prochain livre à paraître sur le sujet, et le premier à paraître entièrement rédigé en français :
Si vous avez déjà passé un peu de temps à chercher de l’aide sur PowerShell, vous connaissez sans doute déjà les auteurs du livre : Arnaud Petitjean et Robin Lemesle sont les fondateurs et les principaux animateurs de l’excellent site powershell-scripting.com.
L’article ci-dessus fournit un résumé du livre et un aperçu de la table des matières. Un lien est également fourni vers Amazon pour précommander le livre (37 €). Pour avoir participé à la relecture de l’ouvrage, je peux témoigner de la pertinence des nombreux exemples fournis et de la densité des explications qui accompagnent le lecteur dans une meilleure compréhension des technologies accessibles grâce à PowerShell (.NET Framework, WMI, ADSI, etc).
Bonnes fêtes de fin d’année !
Janel |
|||
|
|