| Jacques's profilejanelPhotosBlogLists | Help |
|
December 30 Imprimez votre poster Active DirectoryParu dans la revue Technet de mars-avril 2006, ce magnifique poster reprenant sous forme d’un puzzle les composants Active Directory :
Imprimé en grand format (au moins A2 voire A1), ce poster sera un cadeau du plus bel effet pour les étrennes de votre administrateur système préféré !
Merci aux auteurs : Astrid et Martin McClean de Microsoft Australia.
Janel Poussez à fond les possibilités de TabExpansion dans PowerShellMOW, MVP PowerShell toujours très actif et débordant d’imagination, a mis en ligne sa dernière version de TabExpansion. Si vous l’ignorez encore, TabExpansion est une fonction standard de Windows PowerShell qui permet à tout un chacun de personnaliser le comportement de l’expansion automatique de la ligne de commande par la touche de tabulation. Par exemple, vous tapez les premières lettres d’un nom de fichier et vous tapez Tab, et PowerShell complète automatiquement avec le nom de fichier complet.
La nouvelle version de MOW devient une véritable usine à gaz. L’archive à télécharger depuis son site contient pas moins de quatre scripts différents (y compris le script d’installation). Le processus d’installation est un modèle du genre et vaut à lui seul le déplacement. J
N’hésitez pas à franchir le pas. Copiez le contenu de l’archive dans un répertoire de votre choix (qui sera l’emplacement définitif), ouvrez une session PowerShell dans ce répertoire et exécutez PowerTabSetup.ps1. Répondez ‘Y’ à toutes les questions. Ensuite, ouvrez une nouvelle session pour pouvoir profiter des nouveautés. Celles-ci ne sont pas encore décrites dans le détail (un billet plus complet devrait être publié par MOW sur son blog dans les prochains jours) mais quelques exemples sont fournis à la fin de l’installation.
Voilà, tout est dit – ah oui, l’archive est téléchargeable ici :
Naviguez jusqu’à la fin du billet pour trouver la pièce jointe, PowerTab.zip.
Amusez-vous bien !
Janel Gestion des erreurs et déboguage dans PowerShellSi vous avez un peu tâté de l’écriture de scripts avec Windows PowerShell, vous vous êtes sans doute posé la question des techniques disponibles pour gérer les erreurs, notamment en phase de déboguage du script. En fouillant un peu dans les exemples disponibles ici ou là, vous aurez pu comprendre qu’il existe un objet $error, des commandes throw et trap, une commandelette set-psdebug qui répondent à l’essentiel des besoins en la matière. Mais comment tout cela s’articule-t-il dans le cas précis qui vous pose problème?
Malheureusement, la documentation fournie en standard avec la v1.0 de Windows PowerShell est très pauvre sur ce sujet. Vous pourrez trouver quelques informations sur le blog de l’équipe PowerShell mais rien qui puisse fournir une réponse détaillée à un problème précis. Heureusement, les éditions Sapien Press viennent de sortir un livre sur PowerShell (PowerShell: TFM) et comble de bonheur, ils ont décidé de mettre un de leurs chapitres en ligne et devinez quoi ? Il s’agit justement du chapitre sur la gestion des erreurs et le déboguage :
Evidemment, il s’agit d’un livre en anglais, mais une documentation en anglais vaut mieux que pas de documentation du tout, non ? En attendant une prochaine version de PowerShell (la v1.1 ?) qui pourra réparer l’oubli de la v1.0.
Allez, passez de bonnes fêtes de fin d’année !
Janel December 21 Comptez NNTPDans mon précédent billet je décrivais l’utilisation d’une bibliothèque .NET tierce pour communiquer avec des serveurs NNTP. Ma curiosité pour cette bibliothèque était née d’une question de Marco Shaw sur le groupe de discussion Microsoft dédié à Windows PowerShell. Marco avait une idée en tête, et son idée a abouti au script qu’il présente maintenant sur son propre blog :
L’idée est intéressante et la mise en œuvre reste suffisamment simple pour pouvoir être adaptée à de nombreux autres usages. Bien joué Marco !
Janel December 09 A l'attaque des NNTP avec PowerShellRien de belliqueux ici… Je voulais juste continuer l’exploration des ressources du Net avec Windows PowerShell, et j’en profiterai pour illustrer l’installation et l’utilisation de classes tierces et quelques autres techniques.
Je vous propose de vous connecter à un serveur de news utilisant le protocole NNTP, et d’accéder aux ressources de ce serveur comme si vous aviez un véritable lecteur de news. Tout ça depuis votre session Windows PowerShell. D’accord ? Non, il est encore temps de changer de site J. Oui, alors on y va.
Tout d’abord, il faut télécharger la bibliothèque de classes qui nous permettra de communiquer avec un serveur NNTP. Une telle bibliothèque est accessible gratuitement à l’adresse suivante :
Le lien vous redirigera vers le site SourceForge qui offre en téléchargement la bibliothèque avec son code source (Library) et des exemples de programmation en C# (Samples). Seule la bibliothèque est importante pour notre exemple.
Une fois l’archive téléchargée, il faut extraire la DLL ; le fichier qui nous intéresse se trouve dans le répertoire NNTP\bin\release : Smilla.NNTP.dll. Placez ce fichier dans n’importe quel répertoire, en prenant bien soin de le noter J.
Maintenant, repassons dans une session PowerShell.
**********************
Windows PowerShell Transcript Start
Start time: 20061209050709
Username : xxx\xxx
Machine : xxx (Microsoft Windows NT 6.0.6000.0)
**********************
Transcript started, output file is demo.txt
Kézako? PowerShell inclut une commandelette, start-transcript, qui enregistre l’intégralité des entrées-sorties de la session en cours. En l’occurrence j’ai tapé start-transcript demo.txt pour enregistrer la session qui allait suivre dans le fichier demo.txt. Les quelques lignes ci-dessus sont l’en-tête placé automatiquement par start-transcript dans le fichier demo.txt. Le reste de cet article s’appuie sur l’enregistrement contenu dans ce fichier.
Fort logiquement, on arrête l’enregistrement d’une session avec la commandelette stop-transcript. La première chose à faire pour pouvoir utiliser les classes d’une bibliothèque non standard, c’est de charger la bibliothèque en question. Dans le jargon .Net une bibliothèque s’appelle une assembly. On charge une assembly de la façon suivante :
Smilla[7/1]> [reflection.assembly]::loadfrom("c:\users\jbaratho\documents\private\test\smilla\smilla.nntp.dll")
GAC Version Location
--- ------- --------
False v1.0.3705 c:\users\jbaratho\documents\private\test\smilla\smilla.nntp.dll
Pour savoir quelles classes utiliser, plusieurs méthodes sont possibles. En l’occurrence, une documentation (assez succincte) est fournie dans l’archive : regardez dans le répertoire NNTP\Documentation, vous y trouverez un fichier Documentation.chm qui décrit tous les objets (classes, énumérations, etc) disponibles dans cette assembly, et un fichier Tutorial.html qui fournit les bases d’utilisation de ces objets. Cet article s’appuie largement sur ce tutoriel, facilement traduisible en PowerShell.
Une autre méthode possible si la bibliothèque est fournie sans documentation, consiste à utiliser un outil d’exploration de DLL .Net. La référence en la matière est Reflector for .Net. Une fois Reflector téléchargé et démarré, vous pourrez immédiatement parcourir les bibliothèques déjà actives. Vous pouvez également parcourir une bibliothèque non chargée en demandant l’ouverture de la DLL correspondante.
Mais revenons à nos moutons… Je commence par établir une session avec un serveur de news (en l’occurrence, je prendrai le serveur de news de Microsoft) :
Smilla[7/1]> $session = new-object smilla.net.nntpclient.session("news.microsoft.com")
Smilla[7/1]> $session
PostingAllowed ServerInfo Server Port
-------------- ---------- ------ ----
True NNTP Service 6.0.3790.1830... news.microsoft.com 119
Une fois la session établie, je récupère la liste des groupes disponibles. J’en profite pour vérifier combien de groupes j’ai récupérés, et pour pouvoir faire mes tests de lecture/publication, je vérifie s’il existe un groupe dédié aux tests :
Smilla[7/1]> $groups = $session.getnewsgroups()
Smilla[7/1]> $groups.count
2331
Smilla[7/1]> $groups -match "test"
microsoft.public.jp.test
microsoft.public.nntp.test
microsoft.public.test.here
microsoft.public.windowsce.testtools
Le groupe microsoft.public.test.here semble fait pour moi ! Je vais donc me connecter à ce groupe pour en récupérer les articles :
Smilla[7/1]> $test = $groups["microsoft.public.test.here"]
Smilla[7/1]> $test
Session : Smilla.Net.NNTPClient.Session
LastNumber : 7474223
FirstNumber : 7447399
NumberOfArticles : -1
Name : microsoft.public.test.here
PostingAllowed : True
Smilla[7/1]> $articles = $test.getarticles($true,10)
Smilla[7/1]> $articles
<705436AF-7360-49A6-8E5A-9349A8EC50E1@microsoft.com>
<302B79A6-8488-4AFB-99A9-FEEDA1DA7863@microsoft.com>
<D1C13D67-1C19-455F-8202-A46D4BF009F1@microsoft.com>
<7BE20A2D-3032-4C2E-983E-D3641AC388A4@microsoft.com>
<uCHkdf0GHHA.960@TK2MSFTNGP04.phx.gbl>
<ey5Wnf0GHHA.1252@TK2MSFTNGP02.phx.gbl>
<eKnuCg0GHHA.5104@TK2MSFTNGP03.phx.gbl>
<#9dYdg0GHHA.420@TK2MSFTNGP06.phx.gbl>
<845A77C3-D066-4C6D-B119-339ABEE490F1@microsoft.com>
<12011EA4-65AB-490E-ADB0-FEF21F2C9A60@microsoft.com>
Fichtre, quel charabia! Pas de panique. Une lecture dans la documentation me montre que chaque article est en fait composé d’un Header (en-tête) et d’un Body (corps). Il faut donc creuser encore un peu pour avoir plus d’informations :
Smilla[7/1]> $articles.getarticlelist()
NewsGroup Header Body
--------- ------ ----
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header ...
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla.Net.NNTPClient.NewsGroup Smilla.Net.NNTPClient.Header
Smilla[7/1]> $articles.getarticlelist()|ft {$_.header.subject},{$_.header.from},{$_.header.date},{$_.header.lines}
$_.header.subject $_.header.from $_.header.date $_.header.lines
----------------- -------------- -------------- ---------------
oe5 MVP test 0127-1 "Popcorn" <popcorn@microso... 08/12/2006 20:07:33 10
oe5 MVP test 0127-1 "Popcorn" <popcorn@microso... 08/12/2006 20:08:03 10
CsWebNews3Test Test Message Popcorn 08/12/2006 20:10:00 1
CsWebNews3Test Test Message Popcorn 08/12/2006 20:10:01 1
ClusterSentinelNNTPPostTest user@domain 08/12/2006 20:09:46 1
ClusterSentinelNNTPPostTest user@domain 08/12/2006 20:10:02 1
ClusterSentinelNNTPPostTest user@domain 08/12/2006 20:10:48 1
ClusterSentinelNNTPPostTest user@domain 08/12/2006 20:11:33 1
CsWebNews3Test Test Message Popcorn 08/12/2006 20:11:00 1
CsWebNews3Test Test Message Popcorn 08/12/2006 20:12:00 1
Ca commence à faire du sens, non? Maintenant que je sais lire les en-têtes, voyons le corps d’un article en particulier. Au moment où j’ai récupéré les articles, j’ai indiqué que je ne voulais que les en-têtes ($test.getarticles($true,10)où $true indique « ne charger que les en-têtes »). Il faudra donc commencer par récupérer le contenu d’un article avant de pouvoir le lire :
Smilla[7/1]> $article = $articles[0]
Smilla[7/1]> $article.retrievebody()
Smilla[7/1]> $article.body.textbodypart.decodedtext
Testing
--
Popcorn
-----------------------
Sent with Outlook Express 6.00.5027.0
Bien, je crois que je vais pouvoir passer à la publication de ma première contribution à un groupe directement depuis PowerShell. Je commence par la création d’un article à partir de l’objet $session, puis je vais remplir les informations de l’en-tête :
Smilla[7/1]> $post = $session.createarticle()
Smilla[7/1]> $post
NewsGroup Header Body
--------- ------ ----
Smilla.Net.NNTPClient.Header
Smilla[7/1]> $post.header.from = "jbaratho@online.microsoft.com"
Smilla[7/1]> $post.header.subject = "Test from PowerShell w/Smilla"
Smilla[7/1]> $post.header.newsgroups = ,"microsoft.public.test.here"
Vous noterez au passage que la propriété $post.header.newsgroups accepte un tableau de chaînes, car on peut poster sur plusieurs newsgroups en même temps (ce qu’on appelle couramment cross-poster). Comme en l’occurrence je ne posterai que sur un seul groupe, j’indique qu’il s’agit d’un tableau à un seul élément en préfixant le nom du groupe d’une virgule.
Il me reste encore à écrire le corps de mon message. Pour ce faire je vais utiliser une here-string, chaîne qu’on peut composer sur plusieurs lignes en indiquant simplement le début et la fin par les symboles @" et "@ :
Smilla[7/1]> $post.body.text = @"
>> Post from a Windows PowerShell session
>> with Smilla .Net communication library.
>>
>> Jacques
>> "@
>>
Malheureusement, un bug non résolu à ce jour dans PowerShell supprime les lignes vierges d’une here-string. Il existe plusieurs façons de contourner le problème, mais là n’est pas mon propos J. Pour l’heure, vérifions que tout est paré pour la publication :
Smilla[7/1]> $post
NewsGroup Header Body
--------- ------ ----
Smilla.Net.NNTPClient.Header Post from a Windows PowerShell sessi...
Smilla[7/1]> $post|fl *
NewsGroup :
Header : Smilla.Net.NNTPClient.Header
Body : Post from a Windows PowerShell session
with Smilla .Net communication library.
Jacques
Smilla[7/1]> $post.header
Headers : {MIME-Version, Content-Type, Message-ID, From...}
From : jbaratho@online.microsoft.com
Date : 01/01/0001 00:00:00
NewsGroups : {microsoft.public.test.here}
References :
Subject : Test from PowerShell w/Smilla
MessageID : <bdffe1ba-b24e-45e1-9f7d-3766b83238b4@news.microsoft.com>
Path :
FollowupTo :
Expires : 01/01/0001 00:00:00
ReplyTo :
Sender :
Distribution :
Keywords :
Summary :
Approved :
Lines : -1
Xref :
Organization :
MimeVersion : 1.0
Multipart : False
ContentType : text/plain
Charset : us-ascii
Ok, j’envoie :
Smilla[7/1]> $session.postarticle($post)
Et maintenant, je récupère la liste à jour des 10 derniers articles pour vérifier si le mien est arrivé:
Smilla[7/1]> $articles = $test.getarticles($true,10)
Smilla[7/1]> $articles.getarticlelist()|ft {$_.header.subject},{$_.header.from},{$_.header.date},{$_.header.lines}
$_.header.subject $_.header.from $_.header.date $_.header.lines
----------------- -------------- -------------- ---------------
VB MonTool - Public MSCOM NNTP TEST 08/12/2006 20:37:34 1
ClusterSentinelNNTPPostTest user@domain 08/12/2006 20:37:34 1
VB MonTool - Public MSCOM NNTP TEST 08/12/2006 20:37:33 1
ClusterSentinelNNTPPostTest user@domain 08/12/2006 20:37:50 1
Test from PowerShell w/Smilla jbaratho@online.microsoft.com 09/12/2006 04:38:42 1
VB MonTool - Hub MSCOM NNTP TEST 08/12/2006 20:38:41 1
oe5 MVP test 0127-1 "Popcorn" <popcorn@microso... 08/12/2006 20:37:33 10
oe5 MVP test 0127-1 "Popcorn" <popcorn@microso... 08/12/2006 20:38:03 10
CsWebNews3Test Test Message Popcorn 08/12/2006 20:40:01 1
CsWebNews3Test Test Message Popcorn 08/12/2006 20:40:01 1
Bingo! Mon message est bien là, au milieu des autres (j’aurais pu trier par date pour le voir en tête de liste). Je peux donc le récupérer pour en vérifier le contenu :
Smilla[7/1]> $article = $articles.getarticlelist()|?{$_.header.from -eq "jbaratho@online.microsoft.com"}
Smilla[7/1]> $article.retrievebody()
Smilla[7/1]> $article.body.textbodypart.decodedtext
Post from a Windows PowerShell session
with Smilla .Net communication library.
Jacques
Smilla[7/1]>
Voilà. Au souci près du formatage de mon message, l’opération est réussie.
Il me reste à découvrir comment répondre à un article, télécharger/envoyer des fichiers, etc. Mais surtout, au-delà de l’utilisation des news depuis PowerShell, cet exercice m’aura permis de voir comment installer et utiliser une bibliothèque de classes .Net avec PowerShell, ce qui m’ouvre un champ immense d’investigation.
A vous de jouer !
Janel Passez de VBScript à Windows PowerShell sans peineSi vous voulez vous mettre à Windows PowerShell et que vous avez l'habitude d'écrire des scripts en VBScript, et/ou si vous voulez convertir en PowerShell vos scripts VBScript existants, vous devriez trouver cette documentation très utile:
http://www.microsoft.com/technet/scriptcenter/topics/winpsh/convert/default.mspx L’équipe du Script Center a recensé les équivalences en PowerShell pour chaque mot-clé, fonction et opérateur de VBScript (lorsqu'une équivalence était possible, évidemment). Une version téléchargeable de cette documentation est également disponible, avec en prime des exemples de scripts PowerShell. Que demande le peuple ? Janel December 05 Je blogue, tu blogues, il/elle blogue...Tout le monde blogue… Pas vous ? Allons donc, ça ne saurait tarder.
En attendant, voici quelques techniques d’accès aux blogs depuis vos sessions PowerShell. L’utilité ? Peut-être moyenne, peut-être immense, à vous de voir. Pour moi, c’est surtout un moyen d’illustrer les possibilités d’accès aux ressources du Net avec Windows PowerShell, surtout lorsque ces ressources utilisent le format standard XML.
Tout d’abord, le script qui me servira de support pour la suite de mon petit « parcours initiatique » :
get-rssitem.ps1
$myblog = [xml](new-object System.Net.WebClient).DownloadString($args[0])
$myblog.rss.channel.item
Difficile de faire plus court, n’est-ce pas ? Ce script accepte l’URL d’un flux RSS et retourne les éléments disponibles sur le flux en question. Un exemple :
PS> $janel = get-rssitem http://janel.spaces.live.com/blog/feed.rss
PS> $janel.count
23
PS> $janel | get-member -m property
TypeName: System.Xml.XmlElement
Name MemberType Definition
---- ---------- ----------
category Property System.String category {get;set;}
comments Property System.Object[] comments {get;}
description Property System.String description {get;set;}
guid Property System.Xml.XmlElement guid {get;}
link Property System.String link {get;set;}
modified Property System.String modified {get;set;}
pubDate Property System.String pubDate {get;set;}
title Property System.String title {get;set;}
type Property System.Object[] type {get;}
typelabel Property System.String typelabel {get;set;}
Bon, voilà qui est prometteur. Essayons d’en savoir un peu plus :
PS> $janel | ft title,category,pubdate -a
title category pubDate
----- -------- -------
Création d'une requête Windows PowerShell PowerShell Sun, 03 Dec 2006 05:33:50 GMT
Mais qu'est-ce donc qu'un nybble? Ordinateurs et Internet Tue, 14 Nov 2006 03:45:03 GMT
Eliminer les doublons d'une liste avec Windows PowerShell PowerShell Thu, 09 Nov 2006 22:28:14 GMT
Et ou si... PowerShell Wed, 08 Nov 2006 21:08:38 GMT
Le site de Sysinternals maintenant sur microsoft.com Ordinateurs et Internet Wed, 08 Nov 2006 09:30:02 GMT
Retrouver ses petits avec select-string PowerShell Tue, 24 Oct 2006 04:43:17 GMT
…
PS> $janel | group category | sort count –desc
Count Name Group
----- ---- -----
14 PowerShell {guid, guid, guid, guid...}
3 {guid, guid, guid}
3 Ordinateurs et Internet {guid, guid, guid}
2 Vista {guid, guid}
1 ITIL / MOF {guid}
J’aimerais également faire des statistiques basées sur la date de publication, mais la date accessible dans le champ pubDate est une simple chaîne de caractères. Il faudra donc convertir pubDate en [datetime] pour pouvoir en tirer toute la substantifique moelle :
PS> $janel | group {([datetime]$_.pubdate).month}
Count Name Group
----- ---- -----
1 12 {guid}
5 11 {guid, guid, guid, guid...}
5 10 {guid, guid, guid, guid...}
9 9 {guid, guid, guid, guid...}
1 8 {guid}
2 4 {guid, guid}
Et puis, au diable l’avarice, je vais afficher ce résultat de manière un peu plus parlante :
PS> $monthnames = (new-object globalization.datetimeformatinfo).monthnames
PS> $janel | group {([datetime]$_.pubdate).month} | ft @{l="Month";e={$monthnames[$_.name-1]}},count -a
Month Count
----- -----
December 1
November 5
October 5
September 9
August 1
April 2
Pour continuer l’examen du contenu d’un flux RSS, je vais changer de blog et m’intéresser au blog de l’équipe Windows PowerShell :
PS> $wps = get-rssitem http://blogs.msdn.com/powershell/rss.aspx
PS> $wps | get-member -m property
TypeName: System.Xml.XmlElement
Name MemberType Definition
---- ---------- ----------
comment Property System.String comment {get;set;}
commentRss Property System.String commentRss {get;set;}
comments Property System.Object[] comments {get;}
creator Property System.String creator {get;set;}
description Property System.String description {get;set;}
guid Property System.Xml.XmlElement guid {get;}
link Property System.String link {get;set;}
pubDate Property System.String pubDate {get;set;}
title Property System.String title {get;set;}
Le service RSS des blogs MSDN fournit une propriété commentRSS qui renvoie une URL permettant de récupérer de manière récursive les commentaires laissés sur chaque billet. Voyons tout de suite une illustration de son usage :
PS> $wps | foreach {get-rssitem $_.commentRSS} | where {$_.creator -eq "Jacques"}
title : Any localised version of the documentation?
link : http://blogs.msdn.com/powershell/archive/2006/11/30/powershell-documentation-pack.aspx#1192532
pubDate : Sat, 02 Dec 2006 09:11:27 GMT
guid : guid
creator : Jacques
description : <p>Localised versions of PowerShell have localised documentation. Could you make them available as well a
s a separate download? Giving access to the docs without the prerequisite of having to install PowerShell
would help spread the word out to the community.</p>
<p>Thanks,</p>
<p>Jacques</p>
En résumé, le champ d’investigation est immense. La principale contrainte avec les implémentations actuelles de RSS et de XML est qu’elles ne sont pas totalement standardisées. Il vous faudra donc explorer chaque blog pour en découvrir toutes les nuances et pouvoir en exploiter toutes les possibilités. Usez et abusez de la commandelette get-member, c’est votre meilleure arme !
Quoi qu’il en soit, à vous de jouer.
Janel December 03 Création d'une requête Windows PowerShellJe voudrais juste profiter d’une réponse que je viens de faire sur le forum MS dédié à Windows PowerShell pour décrire le processus de création d’une requête que j’ai utilisé dans cette réponse, processus que je trouve très performant, à la fois en temps de développement et en efficacité du résultat.
Mais d’abord, quelle était la question ? William Stacey (MVP C#) demandait comment obtenir la liste des répertoires et sous-répertoires à partir d’un point donné, et ne retourner que le nom de chaque répertoire et sa taille, le tout trié s’il vous plaît (je suppose selon la taille des répertoires).
Les deux premières réponses fournies étaient sans doute très appropriées, mais elles consistaient en un assemblage plus ou moins complexe et relativement long de tests imbriqués avec des tas de variables intermédiaires permettant de récupérer par exemple le nom d’un répertoire ou sa taille une fois l’examen de l’arborescence terminé, pour pouvoir l’ajouter à l’affichage final.
En fait, j’avoue que dès qu’un script dépasse les deux ou trois lignes j’ai un peu de mal à rentrer dedans du premier coup. Il faut que je me rassoie, une tasse de café (ou de Ricoré pour ceux qui connaissent) à la main. C’est vrai, j’exagère un peu, mais pas tant que ça. L’idée que je me fais de PowerShell est qu’un besoin simplement exprimé doit pouvoir obtenir une réponse à peu près aussi simplement exprimée. C’est une idée assez ambitieuse, mais j’aime essayer de m’y tenir.
Me voilà donc devant mon écran à relire la question de William : d’abord, il veut obtenir la liste des répertoires et sous-répertoires à partir d’un point donné. Ok, ça c’est facile :
PS> get-childitem –recurse | where-object {$_.PSIsContainer}
En fait, dans la pratique j’écrirai la ligne ci-dessus avec quelques raccourcis, ce qui donnera ça :
PS> dir –rec|?{$_.PSIsContainer}
dir est un alias de get-childitem, et ? est un alias de where-object. Je n’ajoute pas non plus d’espace avant et après le signe | du pipeline. C’est une question d’économie d’énergie, notion vitale dans les années qui viennent. Pour la clarté des lecteurs, je garderai ces espaces.
Bon, quoi qu’il en soit j’ai les objets que je cherchais. Maintenant, il faut que je les affiche en ne montrant que leur nom et leur taille. Le nom, pas de problème, c’est une propriété de l’objet Directory. Mais la taille n’existe pas sur cet objet. Les fichiers, eux, ont une taille. On peut donc ajouter une propriété à un répertoire, dont la valeur calculée sera la somme des tailles de tous les fichiers contenus dans le répertoire (on pourra même aller dans les sous-répertoires si on le souhaite). A ce moment-là je suis parti sur une fausse piste, ou plutôt sur une piste qui m’a mené dans une impasse.
J’ai voulu afficher les propriétés nom et taille de chaque répertoire en utilisant la commandelette format-table. Mon premier essai a donné ceci :
PS> dir –rec | ?{$_.PSIsContainer} | format-table FullName,@{l="Size";e={(dir $_.fullname –rec | measure length –sum).sum}}
FullName Size
-------- ----C:\Users\jbaratho\Contacts 45757
C:\Users\jbaratho\Desktop 1800
…
Bon, FullName est bien une propriété de l’objet répertoire, mais d’où vient Size ? Si on regarde les valeurs passées à format-table, on notera le deuxième argument passé comme une table de hashage @{}. Seulement, cette table contient plusieurs types de valeurs prédéfinies. On voit ici les deux types les plus courants :
l pour label. La valeur qui suit sera utilisée en tant que label pour cette propriété.
e pour expression. La valeur qui suit sera évaluée comme une expression PowerShell et son résultat, ou plutôt ce qu’elle exprimera sera utilisé comme valeur de la propriété.
Il existe un ou deux autres types permettant notamment de forcer l’alignement de la valeur à droite, au centre ou à gauche, etc.
Je me sers le plus souvent des deux types déjà décrits, et surtout du type e pour expression, qui donne toute sa puissance à cette table de hashage.
Ok, L’affichage de base est correct, il me restait à trier le résultat, facile en collant un sort à la fin du pipeline, non ?
PS> dir –rec | ?{$_.PSIsContainer} | format-table FullName,@{l="Size";e={(dir $_.fullname –rec | measure length –sum).sum}} | sort size –desc
out-lineoutput : Object of type "Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData" is not legal or not in the correct sequence. This is likely caused by a user-specified "format-*" command which is conflicting with the default formatting.
Ah oui, je comprends: les objets retournés par la commandelette format-table sont assez éloignés de la réalité des objets qu’ils affichent. C’est pourquoi on place toujours le formatage en dernière tâche, pour ne pas avoir à en analyser le contenu. Mais là, on ne peut pas faire le tri avant puisque le tri s’appuie sur le résultat de l’expression incluse dans le formatage. Donc, à moins de répéter l’opération contenue dans cette expression aux deux endroits (pendant l’affichage, puis une deuxième fois pour le tri) ce qui sera forcément très pénalisant pour des arborescences un peu chargées, il faut trouver une autre solution.
La solution consistait à remplacer format-table par select-object (qu’on peut raccourcir en select). Les objets passés par select sont des sous-ensembles des objets initiaux, mais ils peuvent au moins très bien répondre au besoin d’affichage et de tri qui leur est demandé. Attaquons donc la transformation :
PS> dir –rec | ?{$_.PSIsContainer} | select FullName,@{l="Size";e={(dir $_.fullname –rec | measure length –sum).sum}} | sort size –desc
Select-Object : Illegal key l
At line:1 char:40
Aargh. La clé l pour Label n’est pas acceptée. Je ne vais donc pas pouvoir donner de nom Size à cette propriété dynamiquement créée, ce qui va compliquer sa référence dans la commandelette sort. Une solution consisterait à reprendre toute l’expression, comme par exemple :
PS> dir –rec | ?{$_.PSIsContainer} | select FullName,{(dir $_.fullname –rec | measure length –sum).sum} | sort {(dir $_.fullname –rec | measure length –sum).sum} –desc
FullName (dir $_.fullname –rec | measure length –sum).sum
-------- ------------------------------------------------
C:\Users\jbaratho\Documents\Private\test\images 653746
C:\Users\jbaratho\Documents\Private\test\sstest 33746
C:\Users\jbaratho\Documents\Private\test\images\thumb 11391
C:\Users\jbaratho\Documents\Private\test\test2 976
…
Ca marche, mais on parcourt l’arborescence deux fois, quel gâchis, et l’entête de la 2e colonne n’est pas super parlante (encore que…). On pourrait quand même faire mieux, si seulement select-object supportait des types équivalents à Label dans une expression… Et pourquoi pas Name, réductible par n? Aussitôt pensé, aussitôt essayé :
PS> dir –rec | ?{$_.PSIsContainer} | select FullName,@{n=”Size”;e={(dir $_.fullname –rec | measure length –sum).sum}} | sort size –desc
FullName Size
-------- ----
C:\Users\jbaratho\Documents\Private\test\images 653746
C:\Users\jbaratho\Documents\Private\test\sstest 33746
C:\Users\jbaratho\Documents\Private\test\images\thumb 11391
C:\Users\jbaratho\Documents\Private\test\test2 976
…
Et là, ça marche aussi! Maintenant, on pourra s’appliquer au fignolage : on peut mettre un format-table à la fin pour régler la taille des colonnes, le type d’affichage de la taille (un peu difficile de lire plusieurs giga-octets de données d’un seul coup d’œil), etc.
Ce qui me plaît, c’est d’être resté dans la conception de base de l’objet demandé : la liste des répertoires et de leurs sous-répertoires. A ceux-ci j’ai appliqué une méthode de calcul de taille qui se glisse dans l’affichage des informations voulues (à savoir nom et taille de chaque répertoire). Et quand on a la liste complète des répertoires et de leur taille, on fait un dernier tri.
Si vous n’êtes pas complètement à l’aise avec les concepts ci-dessus, n’hésitez pas à lire/relire et pratiquer. Posez des questions également. L’intérêt d’un blog repose aussi sur l’interactivité possible avec les interventions des contributeurs et des lecteurs.
Merci,
Janel |
|
|