IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Scripts/Batch Discussion :

avis pour amélioration [PowerShell]


Sujet :

Scripts/Batch

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Rédacteur/Modérateur

    Avatar de gorgonite
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Décembre 2005
    Messages
    10 322
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Transports

    Informations forums :
    Inscription : Décembre 2005
    Messages : 10 322
    Par défaut avis pour amélioration
    Bonsoir à tous,


    Je suis un débutant en PowerShell, et bien que déjà initié au scripting sous Linux (bash/perl), les conventions de scripts sysadmin Windows me sont totalement inconnues... c'est pourquoi je souhaiterais avoir quelques critiques (constructives ) sur ce petit exemple
    (il s'agit de compiler un mémoire en latex avec conversion des eps en pdf histoire d'avoir un pdf utilisable au lieu de la bouse issue de dvips puis ps2pdf... au final l'objectif est de reconstruire un make complet sous forme de DSL)

    je n'aime pas du tout mon utilisation excessive de Invoke-Expression, si vous aviez des méthodes propres pour faire un style de Invoke-Command $commande @args


    la "bibliothèque de fonctions"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    Function Check-Command
    {
        Param(
            [parameter(Position=0,Mandatory=$true)][string] $Command,
            [parameter(Mandatory=$false)][System.Management.Automation.ActionPreference] $ErrAction = [System.Management.Automation.ActionPreference]::Inquire
        )
        Return (Get-Command -CommandType Application -ErrorAction $ErrAction $Command).Definition
    }
    
    Function Replace-Extension
    {
        Param(
            [Parameter(Mandatory=$true,Position=0)][string] $FileName,
            [Parameter(Mandatory=$true,Position=1)][string] $FinalExtension
        )
        $InitialExtension = [regex]::Replace($FileName, "(.+)\.([^\.]+)$", '$2')
        Return (($FileName) -Replace "$InitialExtension$", $FinalExtension)
    }
    
    Function Check-Location
    {
        Param(
            [Parameter(Mandatory=$true,Position=0)][string] $CurrentLocation,
            [Parameter(Mandatory=$true,Position=1)][string] $FilePath
        )
        $Location = Split-Path $FilePath
        If ($CurrentLocation -ne $Location)
        {
            Set-Location $Location
            Return $Location
        }
        Return $CurrentLocation
    }
    
    Function Check-MustBeUpdated
    {
        Param(
            [Parameter(Mandatory=$true,Position=0)][string] $ExistingLocation,
            [Parameter(Mandatory=$true,Position=1)][string] $NewLocation,
            [Parameter(Mandatory=$true,Position=2)] $Test
        )
        If (Test-Path $NewLocation)
        {
            If (& $Test $ExistingLocation (Get-Item $NewLocation))
            {
                Return $false
            }
        }
        Return $true
    }
    
    Function Keep-Location ($code)
    {
        Push-Location
        & $code
        Pop-Location
    }
    le script
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    Param(
        # Programmes externes
        [parameter(Mandatory=$false)][string] $ExecLatex = 'pdflatex',
        [parameter(Mandatory=$false)][string] $ExecBibtex = 'bibtex',
        [parameter(Mandatory=$false)][string] $Converter = 'epstopdf',
        # Paramètres 
        [parameter(Mandatory=$false)][string] $ImgRoot = 'images',
        [parameter(Mandatory=$false)][string] $Filter = '*.eps',
        [parameter(Mandatory=$false)][string] $FinalExtension = 'pdf',
        [parameter(Mandatory=$false)][string] $MainLatex = 'memoire',
        [parameter(Mandatory=$false)][string] $LatexOptions = ' -quiet -halt-on-error -disable-installer '
    )
    
    . .\my_utils.ps1
    
    ########################################################################
    
    ForEach ($prg in $ExecLatex,$ExecBibtex,$Converter)
    {
        Check-Command $prg
    }
    
    $cmdLatex = "$ExecLatex $LatexOptions '$MainLatex'"
    $cmdBibtex = "$ExecBibtex '$MainLatex'"
    
    Keep-Location {
        $currentDir = $ImgRoot
        $test = { Param($old,$new) Return ($new.LastWriteTime -gt $old.LastWriteTime) }
        ForEach ($img in (Get-ChildItem $ImgRoot -Recurse -Include $Filter))
        {
            $currentDir = Check-Location $currentDir $img
            $pdfName = Replace-Extension ($img.Name) $FinalExtension
            If (Check-MustBeUpdated $img $pdfName $test)
            {
                Invoke-Expression ($Converter + ' ' + ($img.Name))
            }
        } 
    }
    
    
    Invoke-Expression $cmdLatex -ErrorAction Stop
    Invoke-Expression $cmdBibtex -ErrorAction Stop
    Invoke-Expression $cmdLatex -ErrorAction Stop
    Invoke-Expression $cmdLatex -ErrorAction Stop

    par avance
    Evitez les MP pour les questions techniques... il y a des forums
    Contributions sur DVP : Mes Tutos | Mon Blog

  2. #2
    Rédacteur


    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    7 171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 7 171
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par gorgonite
    au finale l'objectif est de reconstruire un make complet sous forme de DSL
    Peut-être que PSake peut t'aider...
    Citation Envoyé par gorgonite
    si vous aviez des méthodes propres pour faire un style de Invoke-Command $commande @args
    Invoke-Command est dédié au remoting.
    Citation Envoyé par gorgonite
    je n'aime pas du tout mon utilisation excessive de Invoke-Expression,
    Utilise le pipeline :
    Citation Envoyé par gorgonite
    $cmdLatex,$cmdBibtex,$cmdLatex,$cmdLatex |Invoke-Expression -ErrorAction Stop
    Tu peux aussi manipuler des scriptblocks au lieu de string et les exécuter dans la portée courante avec le point au lieu du ET commercial (&).
    Voir aussi ceci qui peut faciliter le codage au détriment, relatif, du déploiement.

    Dans Check-Command il y a peut être un pb potentiel, car Get-Command peut renvoyer + applis selon le contenu du path, enfin c'est à vérifier.

    Split-path peut poser pb si on utilise un nom de lecteur inconnu.

    Pour la convention de nommage de PS, Test est préférable à Check.

  3. #3
    Rédacteur/Modérateur

    Avatar de gorgonite
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Décembre 2005
    Messages
    10 322
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Transports

    Informations forums :
    Inscription : Décembre 2005
    Messages : 10 322
    Par défaut
    Citation Envoyé par Laurent Dardenne Voir le message
    Peut-être que PSake peut t'aider...
    ok... donc ça existait déjà

    Citation Envoyé par Laurent Dardenne Voir le message
    Tu peux aussi manipuler des scriptblocks au lieu de string et les exécuter dans la portée courante avec le point au lieu du ET commercial (&).
    Voir aussi ceci qui peut faciliter le codage au détriment, relatif, du déploiement.

    je ne vois pas comment transformer "$ExecLatex $LatexOptions" en fermeture attendant comme argument $MainLatex

    à moins que vous ne parliez de faire ceci ?
    auquel cas, je ne vois pas trop l'intérêt d'alourdir ma notation pour aussi peu de gain en expressivité

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $cmdLatex = "$ExecLatex $LatexOptions '$MainLatex'".ExpandString().NewScriptBlock()
    
    ...
    
    . $cmdLatex

    Citation Envoyé par Laurent Dardenne Voir le message
    Split-path peut poser pb si on utilise un nom de lecteur inconnu.

    J'ai mis cela avant... est-ce suffisant ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	If ( -not (Test-Path $FilePath))
    	{
    		Throw "error: $FilePath"
    	}
    Evitez les MP pour les questions techniques... il y a des forums
    Contributions sur DVP : Mes Tutos | Mon Blog

  4. #4
    Rédacteur


    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    7 171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 7 171
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par gorgonite
    auquel cas, je ne vois pas trop l'intérêt d'alourdir ma notation pour aussi peu de gain en expressivité
    Ici l'appel à expandstring est redondant, c'était juste une autre possibilité :-)
    Citation Envoyé par gorgonite
    J'ai mis cela avant... est-ce suffisant ?
    Je pense, reste à le tester.

  5. #5
    Rédacteur/Modérateur

    Avatar de gorgonite
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Décembre 2005
    Messages
    10 322
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Transports

    Informations forums :
    Inscription : Décembre 2005
    Messages : 10 322
    Par défaut
    nouvelle version... avec un New-ScriptBlock (récupéré sur un blog)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    Function Test-Command
    {
        Param(
            [parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)][string] $Command,
            [parameter(Mandatory=$false)][System.Management.Automation.ActionPreference] $ErrAction = [System.Management.Automation.ActionPreference]::Inquire
        )
        BEGIN { }
        PROCESS {
            Return @(Get-Command -CommandType Application -ErrorAction $ErrAction $Command)[0].Definition
        }
        END { }
    }
    
    Function Rename-ItemExtension
    {
        Param(
            [Parameter(Mandatory=$true,Position=0)][string] $FileName,
            [Parameter(Mandatory=$true,Position=1)][string] $FinalExtension
        )
        #$InitialExtension = [regex]::Replace($FileName, "(.+)\.([^\.]+)$", '$2')
        #Return (($FileName) -Replace "$InitialExtension$", $FinalExtension)
        $File = Get-Item $FileName
        $InitialExtension = $File.Extension
        Return ($File.BaseName + '.' + $FinalExtension)
    }
    
    Function Test-MustChangeLocation
    {
        Param(
            [Parameter(Mandatory=$true,Position=0)][string] $CurrentLocation,
            [Parameter(Mandatory=$true,Position=1)][string] $FilePath
        )
        If ( -not (Test-Path $FilePath))
        {
            Throw "error: $FilePath"
        }
        $Location = Split-Path $FilePath
        If ($CurrentLocation -ne $Location)
        {
            Set-Location $Location
            Return $Location
        }
        Return $CurrentLocation
    }
    
    Function Test-MustBeUpdated
    {
        Param(
            [Parameter(Mandatory=$true,Position=0)][string] $ExistingLocation,
            [Parameter(Mandatory=$true,Position=1)][string] $NewLocation,
            [Parameter(Mandatory=$true,Position=2)][scriptblock] $Test
        )
        If (Test-Path $NewLocation)
        {
            If (. $Test $ExistingLocation (Get-Item $NewLocation))
            {
                Return $false
            }
        }
        Return $true
    }
    
    Function Invoke-ScriptBlock
    {
        Param(
            [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0)][scriptblock] $code,
            [switch] $KeepLocation
        )
        if ($KeepLocation)
        {
            Push-Location
        }
        . $code
        if ($KeepLocation)
        {
            Pop-Location
        }
    }
    
    Function New-ScriptBlock
    {
        Param([Parameter(Position=0,Mandatory=$true)][string] $textofscriptblock)
        $executioncontext.InvokeCommand.NewScriptBlock($textofscriptblock)
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    Param(
        # Programmes externes
        [parameter(Mandatory=$false)][string] $ExecLatex = 'pdflatex',
        [parameter(Mandatory=$false)][string] $ExecBibtex = 'bibtex',
        [parameter(Mandatory=$false)][string] $Converter = 'epstopdf',
        # Paramètres 
        [parameter(Mandatory=$false)][string] $ImgRoot = 'images',
        [parameter(Mandatory=$false)][string] $Filter = '*.eps',
        [parameter(Mandatory=$false)][string] $FinalExtension = 'pdf',
        [parameter(Mandatory=$false)][string] $MainLatex = 'memoire',
        [parameter(Mandatory=$false)][string] $LatexOptions = ' -quiet -halt-on-error -disable-installer '
    )
    
    . .\my_utils.ps1
    
    ########################################################################
    
    $ExecLatex,$ExecBibtex,$Converter | Test-Command
    
    $cmdLatex  = New-ScriptBlock "$ExecLatex $LatexOptions '$MainLatex'"
    $cmdBibtex = New-ScriptBlock "$ExecBibtex '$MainLatex'"
    
    Invoke-ScriptBlock -KeepLocation {
        $currentDir = $ImgRoot
        $test = { Param($old,$new) Return ($new.LastWriteTime -gt $old.LastWriteTime) }
        (Get-ChildItem $ImgRoot -Recurse -Include $Filter) | Foreach-Object {
            $currentDir = Test-MustChangeLocation $currentDir $_
            $pdfName = Rename-ItemExtension ($_.Name) $FinalExtension
            If (Test-MustBeUpdated $_ $pdfName $test)
            {
                & $Converter ($_.Name)
            }
        } 
    }
    
    $cmdLatex,$cmdBibtex,$cmdLatex,$cmdLatex | Foreach-Object { 
        Invoke-Command -ErrorAction Stop -ScriptBlock $_ 
    }
    Evitez les MP pour les questions techniques... il y a des forums
    Contributions sur DVP : Mes Tutos | Mon Blog

  6. #6
    Rédacteur/Modérateur

    Avatar de gorgonite
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Décembre 2005
    Messages
    10 322
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Transports

    Informations forums :
    Inscription : Décembre 2005
    Messages : 10 322
    Par défaut
    au passage, vu le grand nombre de fichiers et que chaque conversion eps -> pdf est une tâche "mono-threadée" prenant près d'une minute, j'ai décidé de paralléliser quelque peu le script


    Pour cela, je passe par une routine Foreach-Parallel

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    Function Foreach-Parallel
    {
        Param (
            [Parameter(Mandatory=$true,Position=0)][scriptblock] $Task,
            [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1)] $Data,
            [Parameter(Mandatory=$false)][ValidateScript({$_ -gt 0})][int] $Max = 2
        )
        Begin 
        {
            $jobs =  New-Object System.Collections.ArrayList
        }
        Process
        {
            While ($jobs.Count -ge $Max)
            {
                Wait-Job -Any $jobs | Out-Null
                $finishedJobs =  ($jobs | Where-Object { $_.State -eq 'Completed' })
                $finishedJobs | Foreach-Object { $jobs.Remove($_) }
            }
            $jobs.Add((Start-Job { . $Task $Data }))
        }
        End
        {
            Wait-Job $jobs | Out-Null
        }
    }
    dans cette version, les jobs sont bien lancés, mais il semble que rien ne soit fait (alors que tous les jobs sont bien lancés )

    Je pense toutefois remplacer à terme les Wait-Job par un simple System.Threading.Semaphore

    Edit : 2nde version avec des threads et non des jobs

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    Function New-Thread {
        $config   = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.RunspaceConfiguration
        $runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($config)
        $thread   = New-Object System.Object
    
        $thread | Add-Member NoteProperty "Runspace" $runspace
        $thread | Add-Member NoteProperty "Pipeline" $null
        $thread | Add-Member ScriptProperty "Running" { return ($this.Pipeline -ne $null -and (-not $this.Pipeline.Error.EndOfPipeline -or -not $this.Pipeline.Output.EndOfPipeline)) }
    
        $thread.Runspace.Open()
    
        return $thread
    }
    
    Function Start-Thread {
        param ([object]$thread = $null,[ScriptBlock]$scriptBlock = $(throw "The parameter -scriptBlock is required."))
    
        if ($thread -eq $null) {$thread = New-Thread}
    
        if ($thread.Running) {throw "The thread is already running, please wait for it complete before trying again."}
    
        $thread.Pipeline = $thread.Runspace.CreatePipeline($scriptBlock)
        $thread.Pipeline.Input.Close()
        $thread.Pipeline.InvokeAsync()
    
        return $thread
    }
    
    Function Join-Thread {
        Param ([object]$thread = $(throw "The parameter -thread is required."))
    
        If ($thread.Pipeline -ne $null) {
            while ($true) {
                Read-Thread -thread $thread
                if ($thread.Pipeline.Error.EndOfPipeline -and $thread.Pipeline.Output.EndOfPipeline) {break}
                $thread.Pipeline.Output.WaitHandle.WaitOne(250, $false) | Out-Null
            }
    
            Stop-Thread $thread
    
            if ($thread.Pipeline.PipelineStateInfo.State -eq "Failed") {
                throw $thread.Pipeline.PipelineStateInfo.Reason
            }
        }
    }
    
    Function Foreach-Parallel
    {
        Param (
            [Parameter(Mandatory=$true,Position=0)][scriptblock] $Task,
            [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1)] $Data,
            [Parameter(Mandatory=$false)][ValidateScript({$_ -gt 0})][int] $Max = 2
        )
        Begin 
        {
            $jobs =  New-Object System.Collections.ArrayList
            $pool = New-Object System.Threading.Semaphore $Max,$Max
        }
        Process
        {       
            $jobs.Add((Start-Thread -ScriptBlock { $pool.WaitOne() ; . $Task $Data ; $pool.Release() } ))
        }
        End
        {
            $jobs | Foreach-Object { Join-Thread -thread $_ }
        }
    }
    idem... rien n'est fait


    Qu'en pensez-vous ?


    Evitez les MP pour les questions techniques... il y a des forums
    Contributions sur DVP : Mes Tutos | Mon Blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. avis pour améliorer mon diagramme de classe: gestion des sujets PFE
    Par win_ubuntu dans le forum Diagrammes de Classes
    Réponses: 0
    Dernier message: 12/08/2014, 21h35
  2. Votre avis pour améliorer mon code (opérations matricielles)
    Par Schopenhauer dans le forum Débuter
    Réponses: 42
    Dernier message: 17/09/2010, 19h25
  3. avis pour améliorer mon cv
    Par Nath_k dans le forum CV
    Réponses: 4
    Dernier message: 20/09/2009, 12h45
  4. Réponses: 30
    Dernier message: 05/08/2009, 19h25
  5. [CV] Avis pour améliorer mon cv
    Par lapanne dans le forum CV
    Réponses: 7
    Dernier message: 17/10/2007, 15h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo