Planification de l'exécution d'une commande dans PowerShell

217

Existe-t-il un moyen simple de chronométrer l'exécution d'une commande dans PowerShell, comme la commande «time» sous Linux?
Je suis venu avec ceci:

$s=Get-Date; .\do_something.ps1 ; $e=Get-Date; ($e - $s).TotalSeconds

Mais je voudrais quelque chose de plus simple comme

time .\do_something.ps1
Paolo Tedesco
la source

Réponses:

337

Ouaip.

Measure-Command { .\do_something.ps1 }

Notez qu'un inconvénient mineur Measure-Commandest que vous ne voyez aucune stdoutsortie.

[Mise à jour, grâce à @JasonMArcher] Vous pouvez résoudre ce problème en redirigeant la sortie de la commande vers une commande qui écrit sur l'hôte, par exemple, Out-Defaultpour devenir:

Measure-Command { .\do_something.ps1 | Out-Default }

Une autre façon de voir la sortie serait d'utiliser la Stopwatchclasse .NET comme ceci:

$sw = [Diagnostics.Stopwatch]::StartNew()
.\do_something.ps1
$sw.Stop()
$sw.Elapsed
Keith Hill
la source
114
Vous pouvez également voir une sortie comme celle-ci, Measure-Command {ps | Out-Default}. Ou tout autre élément qui écrit directement sur l'hôte, qui peut être utile ou non.
JasonMArcher
18
J'ai pris cette solution et écrit une fonction qui peut être utile à quelqu'un d'autre. gist.github.com/2206444 - Exemple: time { ping -n 1 google.com } -Samples 10exécutera les 10temps de commande et renverra le temps moyen, minimum et maximum pris. Vous pouvez ajouter -Silentà avaler STDOUT.
joshuapoehls
13
Ma préférence serait d'assigner le résultat de Measure-Command à une variable, comme $t = Measure-Command {<<your command or code block>>}. Essayez et puis tapez $tà l'invite pour voir vos résultats et toutes les propriétés que vous avez accès, comme $t.Milliseconds, $t.TotalSeconds, etc. Ensuite , nous pouvons écrire à ce que la sortie que nous voulons, par exemple,Write-Host That command took $t.TotalSeconds to complete.
Baodad
quoi de plus rapide à utiliser? net.stopwatch, ou mesure-commande, ou simplement comparer deux variables get-date ... (Je veux dire quoi de plus efficace pour conserver en permanence dans un script?)
Hicsy
Peut-être inclure l'essentiel du commentaire de JasonMArcher (il est donc clair qu'il peut être utilisé d'une manière plus fine qu'un script PowerShell entier)?
Peter Mortensen
183

Vous pouvez également obtenir la dernière commande de l'historique et soustraire son EndExecutionTimede son StartExecutionTime.

.\do_something.ps1  
$command = Get-History -Count 1  
$command.EndExecutionTime - $command.StartExecutionTime
Shay Levy
la source
22
Essayez ceci un jour: Get-History | Group {$_.StartExecutionTime.Hour} | sort Count -descpour voir votre modèle d'utilisation de PowerShell par heure de la journée. :-)
Keith Hill
18
+1 pour pouvoir utiliser cela pour savoir combien de temps il a fallu même si vous ne vous attendiez pas à ce que cela prenne du temps lorsque vous avez commencé, vous n'avez donc pas pensé à l'envelopper dans Measure-Command.
Chris Magnuson
3
PowerShell est parfois génial.
ConstantineK
J'aimerais pouvoir vous donner plus qu'un +1 :)
David Ferenczy Rogožan
Oui, c'est super! J'ai fait un one-liner en utilisant:$command = Get-History -Count 1 ; "{0}" -f ($command.EndExecutionTime - $command.StartExecutionTime)
Phil
106

Utilisation Measure-Command

Exemple

Measure-Command { <your command here> | Out-Host }

Le canal vers Out-Hostvous permet de voir la sortie de la commande, qui est par ailleurs consommée par Measure-Command.

Droj
la source
Je pense que vous voulez dire Measure-Command {<your command } | Out-Host - l'Out-Host est en dehors du bloc de script
Peter McEvoy
1
@Peter - il doit être à l'intérieur du bloc, sinon Measure-Command consomme la sortie avant d'aller à la console.
Droj
1
Gotcha ... dans ce cas, vous pourriez même pas besoin du tuyau. Il devrait simplement imprimer les résultats, sauf si vous l'avez enveloppé dans un autre bloc ....
Droj
2
Out-Default est peut-être meilleur que Out-Host car compatible avec les scripts? jsnover.com/blog/2013/12/07/write-host-considered-harmful
MarcH
1
Eh bien, j'ai essayé Out-Default et cela fonctionne très bien dans un terminal aussi, alors pourquoi ne pas toujours utiliser Out-Default? (Je ne l'ai pas essayé dans un script désolé)
MarcH
18

Simples

function time($block) {
    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $sw.Elapsed
}

peut alors utiliser comme

time { .\some_command }

Vous voudrez peut-être modifier la sortie

Mike West
la source
1
Measure-Commandmasque la sortie des commandes, donc cette solution est parfois meilleure.
codekaizen
Il s'agit d'une solution fantastique, qui respecte la sortie de la commande. Vous pouvez également l'invoquer sans accolades pour des commandes simples, par exemple: "time ls", exactement comme vous le feriez sous Unix.
Raúl Salinas-Monteagudo
5

Voici une fonction que j'ai écrite qui fonctionne de manière similaire à la timecommande Unix :

function time {
    Param(
        [Parameter(Mandatory=$true)]
        [string]$command,
        [switch]$quiet = $false
    )
    $start = Get-Date
    try {
        if ( -not $quiet ) {
            iex $command | Write-Host
        } else {
            iex $command > $null
        }
    } finally {
        $(Get-Date) - $start
    }
}

Source: https://gist.github.com/bender-the-greatest/741f696d965ed9728dc6287bdd336874

Bender le plus grand
la source
La question était "Planification de l'exécution d'une commande dans PowerShell". Qu'est-ce que cela a à voir avec le chronométrage d'un processus utilisant Unix?
Jean-Claude DeMars
5
C'est une fonction Powershell que j'ai écrite, qui montre comment calculer le temps d'exécution vous-même plutôt que d'utiliser Measure-Commandou l'une des autres façons de chronométrer l'exécution dans Powershell. Si vous lisez la question d'origine, il a demandé quelque chose qui fonctionne "comme la timecommande sous Linux".
Bender the Greatest
3

Utilisation du chronomètre et du formatage du temps écoulé:

Function FormatElapsedTime($ts) 
{
    $elapsedTime = ""

    if ( $ts.Minutes -gt 0 )
    {
        $elapsedTime = [string]::Format( "{0:00} min. {1:00}.{2:00} sec.", $ts.Minutes, $ts.Seconds, $ts.Milliseconds / 10 );
    }
    else
    {
        $elapsedTime = [string]::Format( "{0:00}.{1:00} sec.", $ts.Seconds, $ts.Milliseconds / 10 );
    }

    if ($ts.Hours -eq 0 -and $ts.Minutes -eq 0 -and $ts.Seconds -eq 0)
    {
        $elapsedTime = [string]::Format("{0:00} ms.", $ts.Milliseconds);
    }

    if ($ts.Milliseconds -eq 0)
    {
        $elapsedTime = [string]::Format("{0} ms", $ts.TotalMilliseconds);
    }

    return $elapsedTime
}

Function StepTimeBlock($step, $block) 
{
    Write-Host "`r`n*****"
    Write-Host $step
    Write-Host "`r`n*****"

    $sw = [Diagnostics.Stopwatch]::StartNew()
    &$block
    $sw.Stop()
    $time = $sw.Elapsed

    $formatTime = FormatElapsedTime $time
    Write-Host "`r`n`t=====> $step took $formatTime"
}

Échantillons d'utilisation

StepTimeBlock ("Publish {0} Reports" -f $Script:ArrayReportsList.Count)  { 
    $Script:ArrayReportsList | % { Publish-Report $WebServiceSSRSRDL $_ $CarpetaReports $CarpetaDataSources $Script:datasourceReport };
}

StepTimeBlock ("My Process")  {  .\do_something.ps1 }
Kiquenet
la source
-2

Juste un mot pour tirer des conclusions (incorrectes) à partir de l'une des commandes de mesure du rendement mentionnées dans les réponses. Il y a un certain nombre d'écueils qui devraient être pris en considération en plus de regarder le temps d'invocation nu d'une fonction ou d'une commande (personnalisée).

Sjoemelsoftware

«Sjoemelsoftware» a été élu mot néerlandais de l'année 2015
Sjoemelen signifie tricherie, et le mot sjoemelsoftware a vu le jour en raison du scandale des émissions de Volkswagen. La définition officielle est "logiciel utilisé pour influencer les résultats des tests".

Personnellement, je pense que " Sjoemelsoftware " n'est pas toujours délibérément créé pour tricher les résultats des tests mais pourrait provenir de situations pratiques accommodantes qui sont similaires aux cas de test comme indiqué ci-dessous.

A titre d'exemple, en utilisant les commandes de mesure du rendement dans la liste, Query Language intégrée (LINQ) (1) , est souvent qualifié de la façon jeûné pour faire quelque chose et il est souvent, mais certainement pas toujours! Quiconque mesure une augmentation de vitesse d'un facteur 40 ou plus par rapport aux commandes PowerShell natives, mesure probablement ou tire une conclusion incorrecte.

Le fait est que certaines classes .Net (comme LINQ) utilisant une évaluation paresseuse (également appelées exécution différée (2) ). Cela signifie que lorsque vous affectez une expression à une variable, cela semble presque immédiatement être fait, mais en fait, il n'a encore rien traité!

Supposons que vous dot-source votre . .\Dosomething.ps1commande qui a soit une expression PowerShell ou une expression Linq plus sophistiquée (pour la facilité d'explication, j'ai directement intégré les expressions directement dans le Measure-Command):

$Data = @(1..100000).ForEach{[PSCustomObject]@{Index=$_;Property=(Get-Random)}}

(Measure-Command {
    $PowerShell = $Data.Where{$_.Index -eq 12345}
}).totalmilliseconds
864.5237

(Measure-Command {
    $Linq = [Linq.Enumerable]::Where($Data, [Func[object,bool]] { param($Item); Return $Item.Index -eq 12345})
}).totalmilliseconds
24.5949

Le résultat semble évident, la dernière commande Linq est environ 40 fois plus rapide que la première commande PowerShell . Malheureusement, ce n'est pas si simple ...

Voyons les résultats:

PS C:\> $PowerShell

Index  Property
-----  --------
12345 104123841

PS C:\> $Linq

Index  Property
-----  --------
12345 104123841

Comme prévu, les résultats sont les mêmes mais si vous avez fait très attention, vous aurez remarqué qu'il a fallu beaucoup plus de temps pour afficher les $Linqrésultats que les $PowerShellrésultats.
Mesurons spécifiquement cela en récupérant simplement une propriété de l'objet résultant:

PS C:\> (Measure-Command {$PowerShell.Property}).totalmilliseconds
14.8798
PS C:\> (Measure-Command {$Linq.Property}).totalmilliseconds
1360.9435

Il a fallu environ un facteur 90 de plus pour récupérer une propriété de l' $Linqobjet, puis le$PowerShell objet et ce n'était qu'un seul objet!

Notez également un autre écueil: si vous recommencez, certaines étapes peuvent apparaître beaucoup plus rapidement qu'avant, car certaines des expressions ont été mises en cache.

En résumé, si vous souhaitez comparer les performances entre deux fonctions, vous devrez les implémenter dans votre cas d'utilisation, commencer par une nouvelle session PowerShell et baser votre conclusion sur les performances réelles de la solution complète.

(1) Pour plus d'informations et d'exemples sur PowerShell et LINQ, je recommande ce site: PowerShell haute performance avec LINQ
(2) Je pense qu'il y a une différence mineure entre les deux concepts, car avec une évaluation paresseuse, le résultat est calculé au besoin en fonction de exécution différée où le résultat est calculé lorsque le système est inactif

le fer
la source
L'intention de cette réponse est de pouvoir renvoyer les gens à une question commune pour une idée fausse générale en ce qui concerne les commandes de synchronisation dans PowerShell, comme je viens de le faire pour une question répétitive comme: Question Powershell - Recherche de la méthode la plus rapide pour parcourir les objets 500k à la recherche d'une correspondance dans un autre tableau d'objets de
500k