Affichage des articles dont le libellé est Power-Shell. Afficher tous les articles
Affichage des articles dont le libellé est Power-Shell. Afficher tous les articles

jeudi 8 mai 2014

Attaquer le port série en PowerShell

J'ai justement l'intention de mettre au point une petite interface machine -> homme avec un Arduino.
L'avantage d'Arduino, c'est qu'il est reconnu comme un port série lorsqu'il il est branché sur le PC.

Je peux donc informer mon petit Arduino d'une tâche à réaliser depuis notre programme en m'adressant à lui directement via le port série :-)

Pour mon grand malheur notre programme date de l'age de pierre (code Clipper compilé en 32bit avec Harbor-project, DB en PostgreSql).
Du coup, j'ai régulièrement l'occasion de déléguer certaines tâches directement à des scripts PowerShell... un bon compromis pour interagir avec le système... la où la fonctionnalité n'est pas accessible via Clipper.

Du coup, je me demandais s'il était possible d'adresser facilement le port série depuis un script PowerShell.
La réponse est oui:
Ecriture sur le port-série:
PS> [System.IO.Ports.SerialPort]::getportnames()
COM3
PS> $port= new-Object System.IO.Ports.SerialPort COM3,9600,None,8,one
PS> $port.open()
PS> $port.WriteLine("Hello world")
PS> $port.Close()

Lecture sur le port série
PS> $port= new-Object System.IO.Ports.SerialPort COM3,9600,None,8,one
PS> $port.Open()
PS> $port.ReadLine()

Souce: Cet article de MSDN


jeudi 27 février 2014

Executer des processus et commandes Shell ou DOS depuis PowerShell

Besoin d'exécuter une commande DOS avec de nombreux paramètre depuis PowerShell?

Hé bien moi aussi... et je dois avouer qu'il y a de quoi s'arracher les cheveux... sauf si l'on sait quel article lire (car il y a quand même de nombreuses méthodes pour y arriver)

  1. Direct - Using the environment path or local folder
  2. Invoke-Expression (IEX)
  3. Invoke-Command (ICM)
  4. Invoke-Item (II)
  5. The Call Operator &
  6. cmd /c - Using the old cmd shell
  7. Start-Process (start/saps)
  8. [Diagnostics.Process] Start()
  9. WMI Win32_Process Create() Method
  10. Stop-Parsing Symbol --%



Toutes ces méthodes sont décrites dans l'article "PowerShell: Running Executables" paru sur le TechNet de MicroSoft.

A consulter attentivement... au risque de tourner en rond pendant longtemps!


Pour moi, la solution fut celle de l'opérateur &

& "C:\Program Files (x86)\PlotSoft\PDFill\PDFill.exe" HeadFoot "c:\stock\Output\PubPdf\AX.pdf" "c:\stock\Output\PubPdf\AX-step1.pdf" 0 0 18 20 18 20 "Ch. orange: Smurf" -align 2 -fontEncode "Occidental" -fontName "Calibri" -fontSize 10.0 -rgbFontColor "0 0 0"

jeudi 10 octobre 2013

ImageMagick et PowerShell

Voici la fin d'un périple d'automatisation de capture d'image pour notre catalogue :-) ...

Donc, après les découvertes suivantes:
  • la capture d'image automatisée sur un Nikon D5000 et 
  • la découverte d'ImageMagick (voir les deux posts précédents)
Voici un script PowerShell qui:
  • Réduit automatiquement l'image en 3 tailles différentes (600x600, 200x200, 100x100)... 
  • Sélectionne la partie centrale (pour avoir un beau carré)
  • Dégrade la qualité à 93% (le meilleur rapport entre taille et qualité)
prêtes à être intégrées dans notre base de donnée PostGreSql:-)
Le reste se fera en clipper compilé à l'aide d'Harbour Project (compilateur Clipper libre)


# ----------------------------------------
# Configuration
$srcfolder = "C:\Stock\Input\CatPhoto.in"
$destfolder = "C:\Stock\Input\CatPhoto.in"
$im_convert_exe = "C:\Program Files (x86)\ImageMagick-6.8.7-Q16\convert.exe"
$src_filter = "pict_*.jpg"
$dest_ext = "jpg"
# Image source = 4282 x 2848, target = ? x 600 (to be cropped at 600x600) 
# Width=903 <-- data-blogger-escaped-final="" data-blogger-escaped-height="600" data-blogger-escaped-source=""> ratio = 4.746 => Width for Height = 600 => 4282 / 4.746 => 903px
# Intermediate image would be resized to 903x600 BEFORE croping to 600x600
$optionsCatImage = "-resize 903 -crop 600x600+151+0 -quality 93" 
$optionsCatThumb = "-resize 302 -crop 200x200+51+0 -quality 93"
$optionsCatMiniThumb  = "-resize 150 -crop 100x100+25+0 -quality 93"
$logfile = "c:\Stock\Input\CatPhoto.in\convert_image.txt"
# ----------------------------------------

$fp = New-Item -ItemType file $logfile -force
$count=0
foreach ($srcitem in $(Get-ChildItem $srcfolder -include $src_filter -recurse))
{
    $srcname = $srcitem.fullname

    # Construct the filename and filepath for the output
    $partial = $srcitem.FullName.Substring( $srcfolder.Length )
    $destCatname = $destfolder + $partial.replace( "pict_", "cat_" )
    $destThumbname = $destfolder + $partial.replace( "pict_", "thumb_" )
    $destMiniThumbname = $destfolder + $partial.replace( "pict_", "mini_" )
    $destCatname= [System.IO.Path]::ChangeExtension( $destCatname , $dest_ext )
    $destThumbname= [System.IO.Path]::ChangeExtension( $destThumbname , $dest_ext )
    $destMiniThumbname= [System.IO.Path]::ChangeExtension( $destMiniThumbname , $dest_ext )
    $destpath = [System.IO.Path]::GetDirectoryName( $destCatname )

    # Create the destination path if it does not exist
    # if (-not (test-path $destpath))
    # {
    #     New-Item $destpath -type directory | Out-Null
    # }

    #--- CAT IMAGE ---
    # Perform the conversion by calling an external tool
    $cmdline =  "& `""+$im_convert_exe+"`""+ " `"" + $srcname  + "`" " + $optionsCatImage + " `"" + $destCatname + "`" "
    echo $cmdline
    invoke-expression -command $cmdline    

    # Get information about the output file    
    $destitem = Get-item $destCatname

    # Show and record information comparing the input and output files
    $info = [string]::Format( "{0} `t {1} `t {2} `t {3} `t {4} `t {5}", $count, 
 $partial, $srcname, $destCatname, $srcitem.Length ,  $destitem.Length)
    echo $info
    Add-Content $fp $info

    $count=$count+1

    #--- CAT THUMB ---
    # Perform the conversion by calling an external tool
    $cmdline =  "& `""+$im_convert_exe+"`""+ " `"" + $srcname  + "`" " + $optionsCatThumb + " `"" + $destThumbname + "`" "
    echo $cmdline
    invoke-expression -command $cmdline    

    # Get information about the output file    
    $destitem = Get-item $destThumbname

    # Show and record information comparing the input and output files
    $info = [string]::Format( "{0} `t {1} `t {2} `t {3} `t {4} `t {5}", $count, 
 $partial, $srcname, $destThumbname, $srcitem.Length ,  $destitem.Length)
    echo $info
    Add-Content $fp $info

    $count=$count+1

    #--- CAT MINI THUMB ---
    # Perform the conversion by calling an external tool
    $cmdline =  "& `""+$im_convert_exe+"`""+ " `"" + $srcname  + "`" " + $optionsCatMiniThumb + " `"" + $destMiniThumbname + "`" "
    echo $cmdline
    invoke-expression -command $cmdline    

    # Get information about the output file    
    $destitem = Get-item $destMiniThumbname

    # Show and record information comparing the input and output files
    $info = [string]::Format( "{0} `t {1} `t {2} `t {3} `t {4} `t {5}", $count, 
 $partial, $srcname, $destMiniThumbname, $srcitem.Length ,  $destitem.Length)
    echo $info
    Add-Content $fp $info

    $count=$count+1
    
}

Outils de transformation d'image ... en ligne de commande ou PowerShell

Introduction
Après le shooting automatique d'image pour notre catalogue (voir article précédent), il faut s'attarder un peu sur le traitement d'image.
Difficile de générer un catalogue en ligne ou papier avec des images qui font 4288x2848 qui font 4.8Mb pièce!!!à plus forte raison s'il y a 2500+ produits dans le catalogue préliminaire.

Gimp et Photoshop c'est bien, mais il n'est pas possible d'imaginer le traitement manuel de 2500 prises de vue.
L'utilisation d'un outil en ligne de commande ou OleAutomation pour automatisé le processus est donc primordial... a plus forte raison si le back-end est maintenu en Clipper.

Découvrez ImageMagick
Au cours de mes recherches, j'ai rencontré ImageMagick, une solution libre et franchement incroyable.

ImageMagick® est une suite logicielle pour créer, éditer, composer ou convertir des images bitmap. Il est capable de lire et d'écrire des images dans une variété de formats (plus de 100) incluant DPX, EXR, GIF, JPEG, JPEG-2000, PDF, PhotoCD, PNG, Postscript, SVG, and TIFF.

Utilisez ImageMagick pour des opérations de modification de taille, flip, mirroir, rotation, distortion, cisaillement et transformation d'images, ajuster les couleurs de l'image, appliquer des effets spéciaux, ou dessiner du texte, lignes, polygones, ellipses et courbes de Bézier.

ImageMagick ne fonctionne pas seulement en ligne de commande, mais aussi avec des objets d'automations et de nombreux Wrapper pour de nombreux langages (y compris Python).

Exemple
Voici une image traitée automatique avec ImageMagick.
La source est en 4288x2848 faisant 4.6Mb

Et son traitement à l'aide d'ImageMagick avec la commande suivante:
Réduction de taille (903x600, réduire la largeur à 903 adapte automatiquement la hauteur à 600), qualité (93%) et découpe de 600x600 au centre (donc éliminer les bords droit et gauche)
convert pict_20131010_104548_1.jpg -resize 903 -crop 600x600+151+0 -quality 93 final.jpg


On obtient:

Ressource

mercredi 9 octobre 2013

Récupérer les images d'un appareil photo en PowerShell

Introduction
La société où je travaille va remonter un vrai catalogue informatisé et nous investissons beaucoup de temps dans la structuration des informations... vive les bases de données.
Mais pour faire un catalogue, il faut aussi des images. 
Pourquoi, donc, ne pas essayer d'automatiser la saisie de ces images catalogue directement depuis notre logiciel (nous avons 2500+ produits au catalogue)..

On place l'objet a photographier sur le stand de photographie, on sélectionne l'article dans le programme et ont clique sur le bouton shoot! Prise de photo, traitement d'image (principalement réduction de taille et poids) et stockage peuvent facilement être automatiser pour un travail d'envergure.

Ha oui, pour corser le tout... le soft est maintenu en clipper compilé en 32 bit sous Windows à l'aide d'Harbour Project

Les appareils "moyen de gamme" c'est super
La plupart des appareil photo moyen de gamme sont reconnu comme de vrai lecteurs disques dès qu'il sont branchés en USB. Sur la plupart il est même possible de faire des prises de vues pendant qu'il est branché en USB.
Même, si dans ce cas, il faut faire les prises de vues en poussant sur le bouton, le fait de disposer d'un système de fichier standard est vachement pratique... car un simple "file copy" rempli toutes nos attentes.

Les appareils "haut de gamme" c'est le casse tête
Par contre, les prise de vue seront faite à l'aide d'un Nikon D5000.
Si l'appareil est super et produit des photographies a tomber par terre, les photos ne sont pas accessibles au travers d'un lecteurs.
Si vous branchez le cable USB, Windows reconnait l'appareil et vous pouvez naviguer à la recherche des photographie avec l'explorateur de fichier... faire un copier/coller.

Mais essayer seulement d'écrire un batch ou un programme qui fait un file copy des fichiers jpeg!!!! vous allez voir que les choses se corsent.

Les appareils haut de gamme sont souvent reconnu par Windows et y associe une interface de gestion "plus évoluée"... cela implique souvent la perte de fonctionnalités de base.

La solution se trouve dans l'interface ActiveX WIA.

Qu'est-ce que WIA
WIA signifie Windows Image Acquisition, c'est une interface d'automation qui permettra de capturer et transférer des images depuis différentes sources (Appareil Photo, WebCam, Scanner, etc).
Avec WIA il est même possible de déclencher la prise de vue sur l'appareil.
Vous trouverez probablement assez facilement des informations sur WIA en naviguant sur le net.

WIA et PowerShell
Il est possible d'utiliser WIA avec PowerShell... c'est bien pratique pour récupérer des images stockées sur notre Nikon D5000.
L'exemple MyShotDownloadPict.ps1 ci-dessous fait une capture d'image (prise de vue) puis rapatrie toutes les images dans un répertoire local du PC... finalement, il effaces ces images de la carte SD (pour la séance suivante).

// Script Powershell pour:
// 
// Utiliser WIA Windows Image Acquisition Automation afin de
// prendre commander le shoot d'une photo (avec Nikon D5000) 
// connecté en USB puis transférer toutes les photos (dont le
// shoot) dans un répertoire sur le PC.
// 
// Nécessite l'enregistrement de WIA 
//   RegSvr32 WIAAut.dll
//
// Source: 
//   http://helvick.blogspot.be/2007/08/camera-automation-with-powershell-part_19.html
//   http://msdn.microsoft.com/fr-fr/library/bb469890.aspx
//
$ErrorActionPreference="silentlycontinue"
$WIAManager = new-object -comobject WIA.DeviceManager
if (!$?) {
   write "Unable to Create a WIA Object"
   Eexit
}
$DeviceList = $WIAManager.DeviceInfos
if ($DeviceList.Count -gt 0) {
   $Device=$DeviceList.item($DeviceList.Count)
} else {
   write "No Device Connected"
}
$ConnectedDevice = $Device.connect()
if (!$?) {
   write "Unable to Connect to Device"
   Exit
}
$Commands = $ConnectedDevice.Commands
$TakeShot="Not Found"
foreach ($item in $Commands) {
   if ($item.name -match "take") {
       $TakeShot=$item.CommandID
   }
}


$Pdir="C:\Stock\Input\CatPhoto.In"
new-item $Pdir -itemtype dir
$ICompare="c:\Program Files\ImageMagick-6.3.5-Q16\compare.exe"
$ActiveCamera = $ConnectedDevice

# Fait un shot et télécharge l'image
$ActiveCamera.ExecuteCommand($TakeShot)
# Attendre 1 seconde pour que l'appareil
# Ai le temps d'enregistrer l'image sur la carte
sleep 1

# Télécharger le restant des images éventuellement présente
$iCount= $ActiveCamera.items.Count
Write "Camera has $iCount pictures"
for ($iCount=$ActiveCamera.items.Count;$iCount -gt 0;$iCount--) {
    
    $f=$ActiveCamera.items.item($iCount)
    $fg=$f.Transfer()
    $localFilename = "pict_"+$(get-date -f "yyyyMMdd_hhmmss")+"_$iCount.jpg"
    Write "Saving $localFilename..."
    $fg.SaveFile( $Pdir+"\"+$localFilename )

    # Retire l'image de la carte de l'appareil photo
    $ActiveCamera.items.Remove($iCount)
}

Voici donc un outil d'automatisation que je pourrais appeler depuis mon programme Clipper (ouf!)

mardi 20 mars 2012

Retourner plusieurs valeurs depuis une fonction - retourner un @HashTable

Introduction
Ce n'est pas la première fois que j'essaye de retourner un HashTable ou un Array dans une fonction.
Et à chaque fois, je n'arrive pas à retrouver le contenu et les méthodes de l'objet.

Je dois avouer être resté circonspect face à ce problème en me demandant pourquoi PowerShell modifiait mon typage au point de ne pas pouvoir récupérer un HashTable via une fonction.

La réponse réside dans le fait qu'une fonction sait retourner plusieurs résultats!
Et que ces résultats peuvent être compilés dans un Array!

Retourner plusieurs résultats dans une fonction
Voici un petit bout de code issu du blog de Matrin Zugec.
La fonction retourne simplement plusieurs valeurs.
Function Get-MultiValue () {
  [hashtable]$Return = @{}
  $Return.Success = $True
  $Return.PercentComplete = 100
  $Return.ReturnTime = $(Get-Date)
  $Return.Username = "Martin Zugec"
  Return $Return
}

L'appel et la réutilisation des valeurs se font comme suit:
$Var = Get-MultiValue
If ($Var.Success) {
  $Var.UserName
  $Var.ReturnTime
}

Super comme truc!

Retourner plusieurs résultats, un comportement par défaut des fonctions
C'est en parcourant ce code et en essayant de le comprendre (en relation avec cet article de Stack-Overflow) que je comprends que le comportement par défaut d'une fonction est de retourner plusieurs valeurs.
Ainsi donc, lorsque qu'une commande affiche quelque-chose à l'écran (par exemple Write-Output) ou que la fonction évalue un objet (par son appel direct, ce qui provoque généralement son affichage), ces derniers éléments sont considérés comme des résultats de la fonction. Ces résultats accompagnerons aussi la valeur retournée avec la commande return.

Ca a l'air compliqué comme ça mais si vous rencontrez des problèmes, pensez simplement à tester la longueur de la variable et a éventuellement itérer le table qu'elle pourrait contenir contient.

En exemple pratique, voici la fonction loadShrWsArticles qui charge le contenu d'un fichier de donnée dans un HashTable.
Cette fonction est censée retourner le HashTable. La HashTable stocke une liste d'objets créés à l'aide de la  fonction CreateShrWsArticleDic (cette dernière crée des dictionnaire d'objet).

L'intérêt réside surtout dans l'utilisation de la fonction (décrite plus bas)

function CreateShrWsArticleDic{
 return @{ "artCode" = ""; "artEan" = ""; "artLabel" = ""; "CBStatus"="" } 
    # Source = File WSys/OutData/WSys-Articles-shrink.dat 
    #    26070-10      -> artCode
    #    8712734085995 -> artEan
    #    4 POLES MALES -> artLabel
    #    0             -> CB Status code
    #    OK            -> CB Status (as text)
    
}

# ------------------------------
#   Load the WSys-Articles-shrink.dat
# ------------------------------
# Returns a dictionnary of ShrWsArticles objects
#
function loadShrWsArticles {
    $sFilename = "C:\Dev\GGTools\WSys\OutData\WSys-Articles-shrink.dat"
    write-progress -activity $("Loading " + (split-path $sfilename -Leaf) ) -status "Openning file" -percentcomplete 0
    Write-Output $("Loading "+$sfilename)
    $hashArticles = @{} # HashTable to store the objects
    $f = Get-Content -Encoding String $sFilename
    # $f[0] -> SourceFile : xxx
    # $f[1] -> CreationDate : yyyyMMdd hh-mm-ss
    $iMax = $f.Length
    $iPos = 2
    while( $iPos -lt $iMax ) {
        # Write Status
     if ( ($iPos % 25) -eq 0 ) {
            write-progress -activity $("Loading " + (split-path $sfilename -Leaf) ) -status ("Progression :"+$iPos+"/"+$iMax ) -percentcomplete $([Math]::Round($iPos/$iMax*100))
     }        
     $a = CreateShrWsArticleDic
     $a.artCode = $f[$iPos]
     $a.artEan = $f[$iPos+1] 
     $a.artLabel = $f[$iPos+2]
        $a.CBStatus = $f[$iPos+3]
        # $a.CBStatusText = $f[$iPos+4]
        
     # Next record 
     $iPos = $iPos + 5
     
     # Add to Hash Table (used simplified key)
        [string]$key = $a.artCode 
        $key = $key.Replace( " ", "" ).Replace( ",", "." ).ToUpper() 
        
     if( $hashArticles.ContainsKey( $key ) ){
      Write-Host "  loadShrWsArticles - hashArticles " $a.artCode " already registered"      
     }
     else {
      $hashArticles.Add( $key, $a )
     }
    }
    write-progress -activity $("Loading " + (split-path $sfilename -Leaf) ) -status "Loaded!" -completed
    Write-Output $("   " +$hashArticles.Count +" items loaded")
    
    return $hashArticles
}

En faisant l'appel
$test = loadShrWsArticles
Je m'attendais à pouvoir retrouver un des éléments de mon dictionnaire.
La ligne $test.Item("B1061403-50").artCode devait logiquement fournir un résultat.
Et pourtant, je reçois obstinément l'erreur:
L'appel de la méthode a échoué parce que [System.Object[]] ne contient pas de méthode nommée « Item ».
Au niveau de ligne : 1 Caractère : 11
+ $test.Item <<<< ("B1061403-50").artCode
    + CategoryInfo          : InvalidOperation: (Item:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

Les choses deviennent claires lorsque l'on comprends que la fonction a retourné un tableau!
Mon HashTable est un des éléments de ce tableau.
> $test.Length
retourne 3, il y a 3 éléments dans mon résultat (en vérifiant chacun d'eux, je retrouve mon HashTable)

> $test[0]
Loading C:\Dev\GGTools\WSys\OutData\WSys-Articles-shrink.dat

> $test[1]
   1916 items loaded

> $test[2]
 Name                           Value                                                                                                                                                                                   
----                           -----                                                                                                                                                                                   
BF632/10A-100                  {artCode, artLabel, artEan, CBStatus}                                                                                                                                                   
MWP6-10                        {artCode, artLabel, artEan, CBStatus}                                                                                                                                                   
1000AROUGE                     {artCode, artLabel, artEan, CBStatus}   
...
...


J'ai retrouvé mon HashTable à la deuxième position du tableau!
Notez que les instructions Write-Output exécutés dans la fonction  font également partie du résultat de la fonction.Je vais enfin pouvoir retrouver mes objets :-) YES!!!

> $monHash = $test[2]
> $monHash.Item("B1061403-50").artCode

 B1061403-50

Cette fois-ci, plus d'erreur :-)

mardi 28 février 2012

Déterminer le type d'encodage d'un fichier en PowerShell

Si vous avez besoin de déterminer le type d'encodage d'un fichier mais que vous ne savez pas comment faire, je vous recommande la lecture de l'article "Powershell get encoding file type" de Franck RICHARD (anglais).

Son script "Get-FileEncoding.ps1" est vraiment très complet et peu s'avérer vraiment pratique.
Encore une petite merveille :-)

vendredi 13 janvier 2012

Détecter les Clé/lecteur USB en PowerShell

Bien que l'instruction Get-PSDrive fournisse déjà une information bien utile (la liste des lecteurs), celle-ci n'était pas suffisante.
En effet, j'ai besoin de localiser un lecteur spécifique (une clé USB) ayant de surcroit un nom de volume spécifique (la bonne Clé USB, celle qui ne nomme "Cruizer").

Voici un petit script vraiment très utile que j'ai trouvé ici sur PowerShell.Com.
Merci à Brian Hagerty pour ce post vraiment très utile.

# DESCRIPTION: Checks to see if a USB logical drive is present, if so, then alerts the user and rechecks 
function CheckFor-USBDrive () { 
    # Gets the USB logical drive  
    $USBDrive_Present = Get-WmiObject -Class Win32_LogicalDisk | Where-Object {($_.DriveType -eq 2) -and ($_.DeviceID -ne "A:")} 
    $count = 0 
         
    # If a USB drive is present then sound 'alert' five times and show message box 
    while($USBDrive_Present) { 
        Write-Host -NoNewline "`a`a`a`a`a" # Alert tone (5) 
        # Checks whether this is the first time the message box is displayed. 
        if($count -lt 1) { 
            Show-MessageBox "ATTENTION: USB Drive" "`nThere is a USB Flash Drive present. Please remove then click OK." 
            $count++ 
        } 
        # If its not the first time then display a different message 
        else { Show-MessageBox "ATTENTION: USB Drive" "`nThe USB Flash Drive was not removed. Please remove then click OK." } 
           
        # Recheck for USB flash drive, then continue while loop if applicable 
        $USBDrive_Present = Get-WmiObject -Class Win32_LogicalDisk | Where-Object {($_.DriveType -eq 2) -and ($_.DeviceID -ne "A:")} 
    } 
} 

jeudi 1 décembre 2011

Manipulation XML en PowerShell

Manipuler XML en PowerShell
PowerShell est capable de lire et de manipuler facilement du contenu XML.
J'ai d'ailleurs trouvé deux excellentes références sur le sujet.
Ou encore:

Comment sauver un HashTable en XML
Il est possible de sauver le contenu d'un HashTable en une seule ligne de code... c'est assez bleufant.
Dans l'exemple suivant,  $list est un HashTable contenant des autres HashTables... voici le contenu de la liste
>>> $__list
Name                           Value                                                                                                                                                                                 
----                           -----                                                                                                                                                                                   
5410439120722                  {ArtCode, EAN, NoteRef, Descr...}                                                                                                                                                       
5410439350358                  {ArtCode, EAN, NoteRef, Descr...}                                                                                                                                                       
5410439354202                  {ArtCode, EAN, NoteRef, Descr...}  

>>> $__list["5410439120722"]
Name                           Value                                                                                                                                                                                   
----                           -----                                                                                                                                                                                   
ArtCode                        BZ1933060080                                                                                                                                                                            
EAN                            5410439120722                                                                                                                                                                           
NoteRef                                                                                                                                                                                                                
Descr                          Vis à tête hex. 8.8 Din 933/934 M 6x 80 zingué                                                                                                                                          
TotalReceiptQty                12 

La sauvegarde se fait à l'aide de la commande suivante:
($__List.Values | convertTo-Xml).Save( "c:\test.xml" )

Et cela produit le résultat suivant:
<?xml version="1.0"?>
<Objects>
  <Object Type="System.Collections.Hashtable">
    <Property Name="Key" Type="System.String">ArtCode</Property>
    <Property Name="Value" Type="System.String">BZ1933060080</Property>
    <Property Name="Key" Type="System.String">EAN</Property>
    <Property Name="Value" Type="System.String">5410439120722</Property>
    <Property Name="Key" Type="System.String">NoteRef</Property>
    <Property Name="Value" Type="System.String" />
    <Property Name="Key" Type="System.String">Descr</Property>
    <Property Name="Value" Type="System.String">Vis à tête hex. 8.8 Din 933/934 M 6x 80 zingué  </Property>
    <Property Name="Key" Type="System.String">TotalReceiptQty</Property>
    <Property Name="Value" Type="System.Int32">12</Property>
  </Object>
  <Object Type="System.Collections.Hashtable">
    <Property Name="Key" Type="System.String">ArtCode</Property>
    <Property Name="Value" Type="System.String">BZ1094050063</Property>
    <Property Name="Key" Type="System.String">EAN</Property>
    <Property Name="Value" Type="System.String">5410439350358</Property>
    <Property Name="Key" Type="System.String">NoteRef</Property>
    <Property Name="Value" Type="System.String" />
    <Property Name="Key" Type="System.String">Descr</Property>
    <Property Name="Value" Type="System.String">Goupille fendue Din 94 * 5,0x 63 zingué  </Property>
    <Property Name="Key" Type="System.String">TotalReceiptQty</Property>
    <Property Name="Value" Type="System.Int32">12</Property>
  </Object>
  <Object Type="System.Collections.Hashtable">
    <Property Name="Key" Type="System.String">ArtCode</Property>
    <Property Name="Value" Type="System.String">BZ1021050015</Property>
    <Property Name="Key" Type="System.String">EAN</Property>
    <Property Name="Value" Type="System.String">5410439354202</Property>
    <Property Name="Key" Type="System.String">NoteRef</Property>
    <Property Name="Value" Type="System.String" />
    <Property Name="Key" Type="System.String">Descr</Property>
    <Property Name="Value" Type="System.String">Rondelle Din 9021 M  5 zingué (5,3/15/1,2)</Property>
    <Property Name="Key" Type="System.String">TotalReceiptQty</Property>
    <Property Name="Value" Type="System.Int32">12</Property>
  </Object>
</Objects>

Le rechargement de la HashTable
Par contre, de toute évidence, le rechargement sera une autre paire de manche.
Ainsi, les données sont rechargées à l'aide de
$xmldata = [xml](Get-Content c:\test.xml) 
Et tous les élement de la la première HashTable visible à l'aide de
$xmlData.Objects.Object[0].Property
Ce qui produit le résultat:
Name                                                                    Type                                                                    #text                                                                  
----                                                                    ----                                                                    -----                                                                  
Key                                                                     System.String                                                           ArtCode                                                                
Value                                                                   System.String                                                           BZ1933060080                                                           
Key                                                                     System.String                                                           EAN                                                                    
Value                                                                   System.String                                                           5410439120722                                                          
Key                                                                     System.String                                                           NoteRef                                                                
Value                                                                   System.String                                                                                                                                  
Key                                                                     System.String                                                           Descr                                                                  
Value                                                                   System.String                                                           Vis à tête hex. 8.8 Din 933/934 M 6x 80 zingué                      
Key                                                                     System.String                                                           TotalReceiptQty                                                        
Value                                                                   System.Int32                                                            12   

Recomposer les paires Clé-Valeur
Pour accéder le contenu des paire clé-valeur de la première HashTable, il faut exécute le code
$xmlData.Objects.Object[0].Property | foreach-object{ if( $_.Name -eq "Key" ){ $KeyName = $_."#Text" } else { $value = $_."#Text"; "$KeyName=$value" }  }

Ce qui produit le résultat:
ArtCode=BZ1933060080
EAN=5410439120722
NoteRef=
Descr=Vis à tête hex. 8.8 Din 933/934 M 6x 80 zingué  
TotalReceiptQty=12

Recomposer une HashTable devient alors assez élémentaire
$ht = @{}
$xmlData.Objects.Object[0].Property | foreach-object{ if( $_.Name -eq "Key" ){ $KeyName = $_."#Text" } else { $value = $_."#Text"; $ht.Add( $KeyName, $value) }  }

L'affichage de la HashTable à l'aide de la commande
$ht
produit le résultat suivant:
Name                           Value                                                                                                                                                                                   
----                           -----                                                                                                                                                                                   
ArtCode                        BZ1933060080                                                                                                                                                                            
EAN                            5410439120722                                                                                                                                                                           
NoteRef                                                                                                                                                                                                                
Descr                          Vis à tête hex. 8.8 Din 933/934 M 6x 80 zingué                                                                                                                                       
TotalReceiptQty                12     

Rechargement de ma liste de HashTable
Sur base du code présenté juste avant, une liste de HashTable telle que présentée en début d'article (une HashTable de HashTable) se recharge comme suit:
$__list = @{}       
       
foreach( $hashTableNode in $xmlData.Objects.Object ) {
  $ht = @{}

   $hashTableNode.Property | foreach-object{ if( $_.Name -eq "Key" ){ $KeyName = $_."#Text" } else { $value = $_."#Text"; $ht.Add( $KeyName, $value) }  }
           
$__list.Add( $ht.EAN.Clone(), $ht.Clone() )

Conversion text vers entier sécurisée (String to Int)

Conversion non sécurisée
La méthode la plus simple de convertir un texte vers sa valeur entière est de faire un casting à la dure.
Bien qu'il existe d'autres méthodes, je vous propose celle-ci en guise d'exemple

$b = [int]"123"
$b
Affiche:
123

Par contre, s'il y a des caractères alphanumérique dans la chaine source, cela se termine en plantage.
Par exemple:
$b = [int]"123a"
Affiche
Impossible de convertir la valeur « 123a » en type « System.Int32 ». Erreur : « Le format de la chaîne d'entrée est incorrect. »
Au niveau de ligne : 1 Caractère : 11
+ $b = [int] <<<< "123a"
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException


Une conversion sécurisée
La conversion sécurisée est obtenue avec l'opérateur "-as". Si la conversion est impossible, le résultat sera simplement Null.
Voici les deux exemples précédents:
$b = "123" -as [int]
$b -eq $null
Affiche
False

La valeur de $b peut-être utilisée directement
$b
Affiche
123

Et maintenant, s'il y a un problème dans la chaine source:

$b = "123a" -as [int]
$b -eq $null
Affiche... notez que la conversion ne génère pas d'erreur :-)
True

Formattage de string en Power-shell

Avec ses nombreux exemple, l'article "Formatting with PowerShell" est vraiment une excellent référence dans le domaine.

Bonne lecture

mercredi 30 novembre 2011

Enumérer les propriétés den PowerShell

Il y a peu, j'ai eu besoin d'obtenir l'énumération des propriétés d'un objet.
En mode interactif, il suffit de faire:
$myObject | Get-Member

Seulement voila, j'en avait besoin en runtime parce que je voulais énumérer les champs d'un DataRow.
Maintenant que je n'en ai plus besoin, j'ai trouvé une magnifique référence sur le net.

Voici l'article "Create A PowerShell Hash Table From A PowerShell Object" qui extrait les propriétés d'un objet et ses valeurs.
Une perle :-)

$myobject | Get-Member -MemberType Properties |
    ForEach {$hash=@{}} {
        $hash.($_.Name) = $myobject.($_.Name)
    } {$hash} 

A noter que l'opération inverse se code comme ceci:
$peeps = @{
  'Lead'='asmith';
  'Enterprise'='bjones';
  'Edge'='chumperdink';
  'Backend'='dwilford';
  'SED'='fhanns'
}            

$obj = New-Object -Type PSObject -Property $peeps

Source:

lundi 21 novembre 2011

Powershell - Interaction Word Excel Access SqlServer

Voici un chouette article de Code Project d'introduction qui indique comment manipuler des sources telles que Excel, Word, ou encore Access ou SqlServeur.

A Quick Guideline for Microsoft Windows PowerShell: Part 3

jeudi 17 novembre 2011

Accéder à une base de donnée DBase / FoxPro (dbf) en PowerShell


La connection string
Avant tout, il faut une "Connection String" permettant de mentionner la DB et le Driver à utiliser.
Une visite sur www.connectionstrings.com est bien utile pour nous aider dans cette tâche.
J'ai retenu l'option suivante (voir cette page)
Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=c:\mydbpath;
Après quelques péripéties, j'en suis arrivé à utilisé un OleDB provider avec la ConnectionString suivante:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\temp\stock;Extended Properties= dBASE IV

PowerShell et Accès DB
Je vous propose les lectures suivantes pour vous familiariser avec l'écriture de code d'accès DB en PowerShell.
Installation des drivers ODBC ou Equivalent
Par defaut, mon Win7 64 bit ne dispose que des drivers ODBC pour Sql Serveur.
Voir  l'outil de configuration "Administration des sources de données ODBC"

J'ai donc besoin d'installer un driver ODBC pour attaquer DBase ou tout équivalent.
Après de longue recherche et m'être éclaté plusieurs fois la tête au mur, j'ai opté pour l'installation du Microsoft Jet Engine disponible sur le net.
Selon certaines sources, l'ACE Engine de Microsoft fonctionnerait très bien aussi.
Dans les faits, je sais qu'il est possible d'attaquer un fichier DBase avec un OleProvider Microsoft.Jet.OLEDB.4.0

Windows 64 bits, PowerShell et OleDB ou ODBC
Nous utilisons Windows Seven 64 bits et Powershell pour attaquer la DB.
Tous mes premiers essais terminait inlassablement par le message " Le fournisseur 'Microsoft.Access.OLEDB.10.0' n'est pas inscrit sur l'ordinateur local " (Arghhh!!! pffff!)
C'est a se taper la tête au mur! Si je regarde dans la base de registre, mon provider OLEDB est bien là!

Mais pourquoi ai-je donc ce message d'erreur?
Et bien mon problème réside dans le fait que j'utilise un OS 64 Bits!
D'un côté, Microsoft à bien produit un provider 64 bits pour SqlServeur mais pas de version 64 bits de Jet Engine (moteur DB d'access).
Office 2010 est toujours un logiciel 32 bit, cela inclus aussi Access et le Jet Engine (et Dieu seul sait quoi d'autre).

D'un autre côté, mon PowerShell fonctionne en 64 bits... et accède uniquement les OleDB Provider 64 bits.
En gros, je suis limité à SqlServeur!

La solution: Utiliser PowerShell 32 bits
Heureusement, ce n'est pas sans solution, en effet, il suffit de se rabattre sur une version 32 bits de PowerShell pour pouvoir accéder librement au OleDB Provider 32 comme Jet Engine.
Et ça marche :-)
Gregory Stike aborde assez clairement ce sujet dans son article "How to Tell if PowerShell is 32-bit or 64-bit"
Pour résumer, il faut:
  • Démarrer un "command prompt" en mode Administrateur
  • Exécuter
    C:\Users\Stock>C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
  • Ne pas oublier de modifier l'exécution policy dans PowerShell si vous voulez exécuter un script
    Set-ExecutionPolicy Unrestricted
Lister les Provider OleDB
L'article "List of OLEDB providers on local or remote computer" présente un petit bout de code qui extrait la liste des OleDB provider enregistré dans la base de registre.
Si j'ai un jour du temps, j'en ferai un script PowerShell.
Un exemple concret
  # On 64 Bits OS, this must be executed with the PowerShell 32 bits
  #   C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
  
  # DB est stockée dans le répertoire c:\temp\Stock
  $ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\temp\stock;Extended Properties= dBASE IV"
  $conn = new-object System.Data.OleDb.OleDbConnection($ConnString)

  $conn.open()

  $cmd = new-object System.Data.OleDb.OleDbCommand("select * from FILE0030",$Conn)
  $da  = new-object System.Data.OleDb.OleDbDataAdapter($cmd)
  $dt  = new-object System.Data.DataTable

  $da.fill($dt)
    Write-Host "Contenu du DataTable"
  $dt 

  $conn.close() 


mardi 15 novembre 2011

Information Windows en Powershell

Voici un petit script qui en dit long sur la version de Windows exécutant le script PowerShell.
Selon les cas, cela peut-être vraiment très intéressant.

   $regset = get-item "HKLM:Software/Microsoft/Windows NT/CurrentVersion"
   foreach( $i in $regset.GetValueNames() ){ 
      Write-Host $i " = " $regset.GetValue($i) 
   }
Ce qui produit le résultat suivant (dont j'ai masqué une partie des valeurs):
CurrentVersion  =  6.1
CurrentBuild  =  7600
SoftwareType  =  System
CurrentType  =  Multiprocessor Free
InstallDate  =  1294140650
RegisteredOrganization  =  
RegisteredOwner  =  UTILISATEUR
SystemRoot  =  C:\Windows
InstallationType  =  Client
EditionID  =  Professional
ProductName  =  Windows 7 Professional
ProductId  =  00371-OEM-8992671-00007
DigitalProductId  =  164 0 0 0 3 0 0 0 48 48 51 55 49 45 79 69 77 45 56 57 57 50 54 55 49 45 48 48 ...
0 0 0 0 0 0 0 0 0 0 138 54 94 179
DigitalProductId4  =  248 4 0 0 4 0 0 0 48 0 48 0 51 0 55 0 49 0 45 0 48 0 48 0 49 0 55 0 56 0 45 0 57 0 50 0 54 0 45 0 55 0 48 0 48 0 48 0 48 0 55 0 45 0 48 0 50 0 45 0 49 0 48 0 51 0 51 0 45 0 55 0 ...
0 0 0 79 0 69 0 77 0 58 0 83 0 76 0 80 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ...
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
CurrentBuildNumber  =  7600
BuildLab  =  7600.win7_ldr.110622-1505
BuildLabEx  =  7600.20994.amd64fre.win7_ldr.110622-1505
BuildGUID  =  0d6bd58e-3df7-4453-87c2-093e51a17572
CSDBuildNumber  =  1
PathName  =  C:\Windows

samedi 12 novembre 2011

PrimalForms: Interfaces graphiques en PowerShell

Les langages de scripting rendent de nombreux services dans le monde Unix depuis des lustres.
Dans un monde dominé par Windows, il était fort regrettable de ne pas disposer d'un outil puissant.
Heureusement, Microsoft à fini par combler cette lacune avec PowerShell... et il faut bien avouer que même arrivé tardivement PowerShell est une réussite aussi aboutie de Bash ou Python.

L'utilisateur Lamba et les scripts
PowerShell c'est beau mais c'est du scripting.
Si cela parle aux administrateurs et développeurs, l'utilisateur final n'y entend absolument rien.
Une interface graphique pour contrôler un script, c'est quand même mieux pour le commun des mortels.
Mais comment faire?

Une solution pour PowerShell
Python dispose de TkInter et quelques essais révèle déjà sa toute puissance.
Il est simple et efficace, visiblement bien adapté aux langages de scripting.
Malheureusement, TkInter ne semble pas exister pour PowerShell (sniff).
Je suis donc parti à la recherche d'un équivalent sur le net et j'ai trouvé la liste d'article suivant:
L'un des articles fait référence à PrimalForms Community Edition, un outil disponible gratuitement (à confirmer), permettant de faire des interfaces graphiques pour PowerShell.

NB:
Cet autre article jette aussi quelques mots sur PrimalForms dans un article.
Selon l'auteur, le résultat serait assez "bluffant".

 

lundi 7 novembre 2011

PowerShell - Excel - Combien de Rows dans la feuille

J'utilise beaucoup PowerShell pour automatiser des scripts de transformation de données Excel.
Il m'arrive régulièrement de ne pas connaître exactement le contenu des feuilles.
Ainsi, j'ai besoin de savoir combien de Rows et Columns sont utilisés dans une feuille Excel.

C'est une question à laquelle il n'est pas facile d'obtenir une réponse.
Maintenant que j'ai enfin trouvé la réponse, je la publie :-)
param([string]$file = $(throw "file parameter required.") )

# Ensure existence of the file
trap [Exception] {
      write-error $("TRAPPED: " + $_.Exception.GetType().FullName);
      write-error $("TRAPPED: " + $_.Exception.Message); 
   write-host "Missing source file: " $file -ForegroundColor Red
   Exit
}

$fullname = resolve-path $file
if( -not( Test-Path $fullname ) ){
 write-host "Missing source file: " $fullname -ForegroundColor Red
 Exit 
}

# Open the Excel file
Write-Host "Ouverture du fichier" -BackgroundColor Yellow
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$excel.Workbooks.Open( $fullname ) 


Write-Host "Extraction des données..." -BackgroundColor Yellow

$sheet = $excel.Sheets.Item(1)
Write-Host $sheet.UsedRange.Rows.count 
# Affiche "4147"
Write-Host $sheet.UsedRange.Columns.count
#Affiche "7" 

mardi 6 septembre 2011

PowerShell - Un outil d'automatisation pas cher

Qu'est ce que PowerShell
Windows PowerShell, anciennement Microsoft Command Shell (MSH), nom de code Monad, est une interface en ligne de commande et un langage de script développé par Microsoft. Il est inclus dans Windows 7 (y compris la version grand public) et fondé sur la programmation orientée objet (et le framework Microsoft .NET).

À l'origine, il était prévu que PowerShell soit inclus dans Windows Vista, mais finalement les deux logiciels ont été disjoints. Microsoft a publié une version beta le 11 septembre 2005, une Release Candidate 1 le 25 avril 2006 et une Release Candidate 2 le 26 septembre 2006. La version finale a été publiée le 14 novembre 2006. Powershell est également inclus dans Microsoft Exchange Server 2007, sorti au quatrième trimestre 2006, ainsi que la plupart des produits Microsoft sortis depuis.

PowerShell est compatible avec toutes les versions de Windows qui supportent la version 2.0 de .NET.

Depuis le 24 mars 2009, PowerShell 1.0 pour Windows XP et Vista est distribué comme une mise à jour logicielle facultative par le service Windows Update de Microsoft. Il est intégré nativement dans Windows 7 en version 2.01.
Source: Wikipedia

PowerShell un outil d'automatisation
Tout développeur et utilisateur Unix sait fort bien qu'un bon langage de scripting est essentiel, sinon vital, en ce qui concerne l'administration au jour le jour.
Ma société ne disposant pas de gros moyens pour ses investissements informatique (y compris en programmation), je me suis penché sur le PowerShell de Microsoft.
Maintenant disponible en standard avec Windows, sa mise en place et exploitation devient vraiment aisé.

Derrière des concepts orienté objet dans lequel il est nécessaire de faire des investissement, PowerShell se révèle être un outil prodigieux.
Que les Unixiens ne se froissent pas, le shell Unix à enfin son équivalent sous Windows.

A l'heure ou j'écris ces lignes, j'utilise PowerShell pour faire:
  • De la manipulation de fichier (en masse)
  • De la lecture et traitement de fichier XML (sur base du contenu)
  • De la transformation XSL
  • Des lignes de commandes (cmdlet PowerShell).
Je n'en reviens toujours pas des résultats obtenu et l'investissement ne fut vraiment pas si terrible que cela.
PowerShell est donc un outil plus que recommandable.
Voila enfin un outil à la hauteur des administrateurs et des développeurs !

PowerShell GUI
Il faut reconnaître que si PowerShell dispose d'une interface interactive, faire du développement notepad et test en ligne de commande peut vite se révéler pesant.
Il existe un formidable outil appelé PowerGUI (powergui.org) disponible en freeware.
S'il est un peu massif, il rend l'utilisation et le développement des scripts PowerShell beaucoup plus agréable.

Source: blog de Ravikanth Chaganti




Ressources