| Jacques's profilejanelPhotosBlogLists | Help |
|
October 19 Comparer deux fichiers texte avec PowerShellSur le forum de PowerShell-Scripting, quelqu’un demandait récemment comment faire pour comparer les lignes de deux fichiers texte et obtenir le nombre de caractères différents. Bien sûr, l’utilisation du terme « comparer » m’a sauté aux yeux comme une évidence : compare-object !
Pour des raisons trop longues à expliquer ici (je viens de découvrir que Live.com impose une limite à la taille des billets, limite que j'ai apparemment franchie avec la première version de ce billet), la solution reposant sur compare-object ne marchait pas. Vous trouverez les explications sur le forum en question. Je suis donc arrivé au script suivant :
# -- compare-textfile.ps1 --
param ( $reference, $difference )
$file1 = [System.IO.File]::OpenText((dir $reference)) $file2 = [System.IO.File]::OpenText((dir $difference))
$line = 1
while (!$file1.EndOfStream -or !$file2.EndOfStream) { $line1 = $line2 = "" ($file1.EndOfStream) -or ($line1 = $file1.ReadLine()) > $nul ($file2.EndOfStream) -or ($line2 = $file2.ReadLine()) > $nul
$length = $line1.length if ($line2.length -gt $length) { $length = $line2.length }
$matchline = New-Object PSObject $chararray = @() $diff = 0
for ($i=0;$i -lt $length;$i++) { if ([char]$line1[$i] -ne [char]$line2[$i]) { $diffchars = New-Object PSObject $diffchars | Add-Member -MemberType NoteProperty Position $i $diffchars | add-member -MemberType NoteProperty Reference $line1[$i] $diffchars | add-member -MemberType NoteProperty Difference $line2[$i] $chararray += $diffchars $diff++ } }
$matchline | Add-Member -MemberType NoteProperty Line $line $matchline | Add-Member -MemberType NoteProperty Different $diff $matchline | Add-Member -MemberType NoteProperty Equal ($length - $diff) $matchline | Add-Member -MemberType NoteProperty Characters $chararray $matchline
$line++ }
$file1.Close() $file2.Close()
# -- fin de script --
Imaginons deux fichiers texte simples :
PS> type ref.txt HELLO WORLD HOULA HOUP
PS> type dif.txt HELLO WORLD! HOULA, HOUPS...
PS> compare-textfile ref.txt dif.txt
Line Different Equal Characters ---- --------- ----- ---------- 1 1 11 {@{Position=11; Reference=... 2 10 5 {@{Position=5; Reference= ...
Le résultat s’affiche ligne par ligne. On peut voir que sur la première ligne on a un caractère de différent et onze caractères égaux (Equal). Sur la deuxième ligne, j’ai dix caractères différents et cinq égaux. Mais quelles sont ces différences ? Et qu’est-ce que c’est que ce charabia à la fin de chaque ligne ?
PS> $comp = compare-textfile ref.txt dif.txt PS> $comp[0].characters
Position Reference Difference -------- --------- ---------- 11 !
La propriété Characters d’une ligne (ici, la première) est en fait un tableau qui contient la position de chaque différence, et pour chaque position le caractère de référence (celui présent ou pas à la position donnée dans le premier fichier passé en paramètre) ainsi que le caractère de différence (celui présent ou pas à la même position donnée dans le deuxième fichier passé en paramètre).
Attention : les numéros de ligne commencent à 1, mais les positions dans chaque ligne comment à 0. J’ai fait ce choix pour suivre les conventions les plus fréquemment rencontrées dans les numérotations de ligne : ouvrez un fichier dans un éditeur de texte évolué, vous verrez que la numérotation des lignes commence toujours à 1. A l’inverse, la numérotation des caractères dans une chaîne PowerShell commence toujours à 0.
Mais revenons à notre exemple. Regardons maintenant la deuxième ligne :
PS> $comp[1].characters
Position Reference Difference -------- --------- ---------- 5 , 6 H 7 O H 8 U O 9 P U 10 P 11 S 12 . 13 . 14 .
L’insertion de la virgule en cinquième position a déplacé la fin du texte ce qui provoque une cascade de différences jusqu’aux points de suspension.
Très bien, mais le problème initial consistait à obtenir le nombre total de différences entre les deux fichiers. Or, ici je n’affiche les différences que ligne par ligne. Bigre, comment faire ? Voyons voir :
PS> compare-textfile ref.txt dif.txt | measure-object different –sum
Janel October 11 Faites votre BA du jourVous n’avez pas encore fait votre bonne action aujourd’hui ? Voici l’occasion de vous racheter et de finir la journée en beauté. Votez pour la photo que j’ai publiée sur http://www.picture.com, vous me ferez peut-être gagner un iPod Shuffle :
Et si je gagne l’iPod, je serai plus heureux, donc en meilleure forme, donc plus créatif sur mon blog. Donc un bénéfice également pour vous. Ca c’est du gagnant-gagnant ou je ne m’y connais pas !
Merci d’avance,
Janel October 09 Un éditeur de scripts PowerShell gratuit et super efficaceVoilà une bonne nouvelle qui fait plaisir ! La dernière version de PowerGUI, console d’administration en mode graphique basée sur PowerShell, inclut maintenant un éditeur de scripts à la fois simple et efficace, le PowerGUI Script Editor :
Parmi les fonctionnalités les plus pratiques que j’ai pu relever après quelques minutes d’utilisation :
Pour compléter ce tableau, trois excellentes nouvelles :
Alors, qu’attendez-vous ? J
Janel October 03 Je veux ajouter un bouton Imprimer à mon Windows Form créé à partir de PowerShellIl y a quelque temps sur le forum de PowerShell-Scripting un utilisateur a demandé comment il pouvait imprimer un Windows Form qu’il avait créé à partir de PowerShell. Le premier lot de réponses qu’il a reçues tournaient autour de la possibilité de faire une capture de la fenêtre dans le presse-papiers de Windows, et d’envoyer le contenu du presse-papiers vers l’imprimante. La solution est ingénieuse, mais elle repose sur plusieurs points difficiles à résoudre. En premier lieu, il faut pouvoir gérer le presse-papiers, ensuite il faut pouvoir interagir avec l’imprimante et notamment savoir lui transmettre l’image capturée dans un format qu’elle comprenne.
Dans une telle situation, on peut aussi ajouter au formulaire un bouton "Imprimer" qui appellera les méthodes du Framework .NET qui vont bien. Il existe un exemple sur MSDN pour imprimer un Windows Form, mais le code fourni n’est évidemment pas en PowerShell mais en VB.Net et en C#. J'ai donc dû gratter un peu pour pouvoir le traduire en PowerShell. Voici ce que ça donne - le code qui suit suppose que le Windows Form s'appelle $form :
# [void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")[void] [Reflection.Assembly]::LoadWithPartialName("System.Drawing") $printClick = { $myGraphics = $form.CreateGraphics() $size = $form.Size $script:memimage = new-object System.Drawing.Bitmap $form.Width, $form.Height, $myGraphics $memgraphics = [System.Drawing.Graphics]::FromImage($script:memimage) $memgraphics.CopyFromScreen($form.Location.X, $form.Location.Y, 0, 0, $size) $printdoc.Print()} $PrintPage = { $_.Graphics.DrawImage($script:memimage, 0, 0)} $printDoc = New-Object System.Drawing.Printing.PrintDocument$printDoc.Add_PrintPage($PrintPage) $printButton = New-Object System.Windows.Forms.Button$printButton.Text = "Imprimer"$printButton.Add_Click($printClick) # $form = New-Object System.Windows.Forms.Form$form.Controls.Add($printButton)# $form.ShowDialog() Les lignes commentées sont simplement là pour rappeler ce qui doit être fait par ailleurs pour créer et afficher le Windows Form. Le script existant doit déjà contenir des lignes équivalentes.
Au-delà du besoin spécifique de lancer une impression, cet exemple vous montrera également comment associer une action à un évènement qui survient sur un contrôle. Ici, nous avons l’action d’impression initiée par un clic sur le bouton $printButton et dans cette action d’impression la « sous-action » d’impression d’une page initiée par l’appel à la méthode Print() de l’objet $printDoc, respectivement associées aux blocs de commandes $printClick et $printPage.
Il ne vous reste plus qu’à l’adapter à vos propres besoins et à laisser libre cours à votre fantaisie !
Janel October 01 Infos photos (et plus) avec PowerShellEn parcourant la collection de scripts PowerShell qui commence à envahir mon disque dur, j’ai retrouvé ce script, get-filemetadata.ps1, qui récupère les méta-données d’un fichier. Ces méta-données sont des informations sur le contenu d’un fichier, généralement renseignées par l’application qui l’a créé.
Ainsi, les méta-données d’un document Word incluent le titre (information à distinguer du nom du fichier), le nombre de mots, le nombre de pages ou encore le temps total passé à éditer le document (pas sûr que cette information soit totalement pertinente). Les méta-données d’une photo numérique incluent la résolution de l’image, le temps de pose, l’ouverture du diaphragme ou encore le matériel utilisé.
Je ne suis pas l’auteur de ce script. Je me rappelle avoir vu ce sujet traité sur plusieurs blogs (celui de MOW notamment, ainsi que le blog d’un membre de l’équipe PowerShell) mais le script que j’ai n’étant pas signé il m’est impossible d’identifier à coup sûr son origine. Qui plus est, il est probable que je l’aie personnalisé après l’avoir téléchargé. Bref, la version que voici est – sauf preuve du contraire – le premier « script traditionnel » publié ici, comme on a des « chants traditionnels » dans notre patrimoine :
# get-filemetadata.ps1
# # Auteur: traditionnel, adapté par janel
#
begin {
function emitMetaInfoObject($path) {
[string]$path = (resolve-path $path).path
[string]$dir = split-path $path
[string]$file = split-path $path -leaf
$shellApp = new-object -com shell.application
$myFolder = $shellApp.Namespace($dir)
$fileobj = $myFolder.Items().Item($file)
$metaInfoObj = new-object psobject
$metaInfoObj.psobject.typenames[0] = "Custom.IO.File.Metadata"
for ( $i=0 ; $i -lt 300; $i++) {
$n = $myFolder.GetDetailsOf($null, $i)
if ($n) {
$v = $myFolder.GetDetailsOf($fileobj,$i)
if ($v) {
$metaInfoObj | add-member noteproperty $n $v
}
}
}
$metaInfoObj
}
}
process {
if ($_) {
emitMetaInfoObject $_.Fullname
}
}
end {
if ($args) {
$paths = @()
foreach ($path in $args) {
if (!(test-path $path)) {
write-error "$path is not a valid path"
}
$paths += resolve-path $path
}
foreach ($path in $paths) {
emitMetaInfoObject $path
}
}
}
# end of script
Voyons un peu ce que ça donne sur un album de Lloyd Cole :
PS> dir | get-filemetadata | format-table artists,year,album,title,length -auto
Artists Year Album Title Length
------- ---- ----- ----- ------
Lloyd Cole and the Commotions 1985 Easy Pieces Rich 00:04:22
Lloyd Cole and the Commotions 1985 Easy Pieces Why I Love Country Music 00:03:00
Lloyd Cole and the Commotions 1985 Easy Pieces Pretty Gone 00:03:32
Lloyd Cole and the Commotions 1985 Easy Pieces Grace 00:04:05
Lloyd Cole and the Commotions 1985 Easy Pieces Cut Me Down 00:04:29
...
Sur des photos, ça donne les résultats suivants :
PS> dir *.jpg | get-filemetadata | ft name, "date created", "camera model", "focal length", "f-stop", exposure* -a
Name Date created Camera model Focal length F-stop Exposure bias Exposure time
---- ------------ ------------ ------------ ------ ------------- -------------
001.JPG 13/01/2007 17:21 NIKON D50 55 mm f/5,6 0 step 1/60 sec.
Aéroport 2.jpg 10/03/2007 09:37 NIKON D50 40 mm f/4,5 0 step 1/125 sec.
Aéroport.jpg 10/03/2007 09:34 NIKON D50 40 mm f/4,5 0 step 1/125 sec.
BW 1.jpg 28/02/2007 10:53 NIKON D50 35 mm f/2 -0,3 step 1/250 sec.
BW 2.jpg 28/02/2007 11:00 NIKON D50 35 mm f/2 -1 step 1/250 sec.
...
Maintenant, si vous voulez vraiment exploiter les données ci-dessus à des fins statistiques par exemple, comme calculer la durée totale de tous les morceaux de musique de votre compositeur préféré, ou encore estimer la longueur de focale moyenne de vos photos, vous devrez appliquer un petit traitement aux données retournées par le script. Rien de bien méchant, mais en gros il faudra enlever le texte superflu (comme les ‘mm’ de la longueur focale) ou encore convertir le format utilisé pour l’affichage en une valeur compréhensible par PowerShell (comme ’00:04:22’ pour la durée d’un morceau de musique).
De plus, ce script est conçu pour traiter les méta-données de n’importe quel type de fichier. L’avantage est évident, mais si vous manipulez des photos couramment, vous devez savoir que les méta-données qu’une photo numérique peut stocker sont considérablement plus nombreuses. Ces données, qu’on appelle également données EXIF, peuvent être visualisées dans la plupart des logiciels de traitement d’images, ou encore en ligne de commande avec un outil gratuit fabuleux : ExifTool.
ExifTool permet de très nombreuses choses qu’il serait difficile d’implémenter dans un script PowerShell sans avoir recours à une bibliothèque de fonctions externe. Je n’ai pas encore trouvé la bibliothèque en question, ni trouvé le courage ou le temps de m’y coller, alors voici une méthode à deux balles pour – au moins – exploiter les informations retournées par ExifTool depuis un script PowerShell :
filter get-exifdata {
$img = new-object PSObject
exiftool $_.FullName | foreach {
$n,$v = $_.split(":",2) | foreach {$_.trim()}
$img | add-member NoteProperty $n $v -ea SilentlyContinue
}
$img
}
Pour que la fonction ci-dessus marche, il faut que vous ayez téléchargé et copié exiftool.exe dans un répertoire déclaré par votre PATH.
Vous pouvez ensuite l’utiliser ainsi :
PS> dir *.jpg | get-exifdata
Tel quel, l’affichage risque d’être assez rébarbatif et finalement peu différent de ce que vous donnerait ExifTool directement. Mais comme pour get-filemetadata, on pourra utiliser les commandelettes standard pour filtrer, trier, mesurer, grouper et formater les données émises. Déjà, essayons cela :
PS> dir *.nef | get-exifdata | ft "focal length",aperture,"focus distance",depth* -a
Focal Length Aperture Focus Distance Depth of Field
------------ -------- -------------- --------------
200.0mm 5.6 33.50 m 6.31 m (30.64 - 36.95)
70.0mm 11.0 21.13 m 389.65 m (10.85 - 400.50)
18.0mm 11.0 1.68 m inf (0.79 m - inf)
18.0mm 11.0 1.68 m inf (0.79 m - inf)
18.0mm 11.0 1.68 m inf (0.79 m - inf)
...
On constate qu’ExifTool obtient des informations sur des fichiers RAW (*.NEF est l’extension de fichier pour le format RAW de Nikon) que get-filemetadata ne sait pas obtenir – ici par exemple, « Focus Distance » qui indique la distance entre l’appareil photo et le point sur lequel la mise au point a été faite, et « Depth of Field » qui indique la profondeur de champ avec entre parenthèses le point le plus proche et celui le plus distant entre lesquels la netteté est considérée comme acceptable.
En faisant une petite manipulation sur les valeurs de la propriété « Focus Distance », on pourra récupérer un nombre réutilisable pour nos propres calculs. Voici comme dernier exemple une ligne de commande qui va mesurer la distance de mise au point moyenne sur toutes les photos (au format RAW) dans le répertoire en cours :
PS> dir *.nef | get-exifdata | select @{n="Distance";e={$_."Focus Distance".Split()[0]}} | measure-object distance –average –min -max
Count : 186
Average : 10,8857526881721
Sum :
Maximum : 33,5
Minimum : 0,5
Property : Distance
Voilà, maintenant je sais que sur les 186 photos de cette série, en moyenne j’ai mis au point à 10,88m de distance. Essentiel, non ?
A vous de jouer!
Janel |
|
|