【问题标题】:Measure runtime of start-process with parameters使用参数测量启动过程的运行时间
【发布时间】:2019-08-17 19:18:10
【问题描述】:

我有一个运行以下命令的脚本:

$result = Start-Process -Wait -PassThru $proc $args

我想测量需要多长时间。基于this article,我正在尝试以下操作:

$buildtime = measure-command -expression { $result = Start-Process -Wait -PassThru $proc $args }

不幸的是,它抛出了以下错误:

Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null, empty, or an element of
the argument collection contains a null value. Supply a collection that does not contain any null values and then try
the command again.
At C:\!scripts\pullandbuild.ps1:78 char:90
+ ... d -expression { $result = Start-Process -Wait -PassThru $proc $args }
+                                                                   ~~~~~
    + CategoryInfo          : InvalidData: (:) [Start-Process], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartProcessCommand

由于我在字符串和变量扩展方面遇到的其他问题,$args 的定义如下:

$args = @"
& -quit -batchmode -logFile "$logfile" -buildWindows64Player "$($fullpath)\Project.exe"
"@

我该如何解决这个问题?还是有另一种方法来捕获脚本的运行时?我正在尝试学习 PS,所以我宁愿避免在运行之前和之后捕获时间并进行比较,但如果这是我唯一的选择,我会的。

【问题讨论】:

标签: powershell parameter-passing


【解决方案1】:

stivix's answer 包含关键指针 - $args 是一个不应用作自定义变量的automatic variable - 但值得深入挖掘:

$args 自动设置为在该代码单元内传递给simple script [block] or function[1] 的位置参数数组 ([object[]]);如果没有传递任何参数,$args 包含一个空数组。

由于您无法传递旨在被传递给Measure-Command-Expression 参数的脚本块看到的参数,因此$args 定义为该脚本块内的空数组,这导致Start-Process 失败。

顺便说一句:Start-Process fails 带有一个空数组 - 而不是将其解释为“没有要传递的参数” - 这是 Windows PowerShell 中的一个问题此后已在 PowerShell Core 中得到更正。

PowerShell 甚至允许您$args 赋值是有问题的。 不幸的是,这是无法阻止自定义使用自动变量的更大问题的一部分 - 请参阅this answer

这种行为的阴险之处在于,在您输入脚本 [块] 或函数之前,问题不会浮出水面;在调用者的范围内,分配给$args的自定义值确实生效,如下例所示:

$args = 'hi'  # !! To be avoided. Assign a custom value to $args
# Echo the custom value, in the same scope.
"after assignment: [$args]"         
# Call a script block and echo $args there. 
& { "in script block: [$args]; element count: $($args.Count)" }  

以上产量:

after assignment: [hi]
in script block: []; element count: 0

第 1 行输出显示 调用者 范围内的分配按预期工作。

第二个输出行显示$args 在进入脚本块时被自动设置为一个空数组,因为没有参数被传递给脚本块 ({ ... })。

注意:从技术上讲,local $args 变量是在脚本块内创建的,如果您显式访问调用者的范围(例如通过 (Get-Variable args -Scope 1 -ValueOnly)),调用者的副本仍然可以访问,但是更明智的做法是完全避免自定义使用 $args


[1] $args 仅包含那些未绑定到显式声明 参数(如果有)的参数。在advanced script or functions 中,您只能将参数传递给显式定义的参数,因此$args 没有在那里定义,因为它的使用毫无意义;事实上,在高级脚本/函数中,$args 确实 保留调用者的 custom $args 定义,如果在同一范围域中运行,但这不应该被依赖上。

【讨论】:

    【解决方案2】:

    编辑:我刚刚检查过,你的 $args 变量是空的,原来 $args 是一个内置变量。一旦我更改了变量名,它就不再为空了,试试吧。

    您不妨试试以下方法之一:

    1.

    $startTime = Get-Date
    $processToRun = Start-Process notepad.exe -PassThru -Wait
    $finishTime = Get-Date
    $timeDifference = New-TimeSpan -Start $startTime -End $finishTime
    
    1. 使用 $result 的 StartTime 和 ExitTime 属性,然后使用 New-Timespan 比较时间

    【讨论】:

    • 其他方法:使用Get-Date 很方便,尤其是如果您确实想查看命令输出,但请注意最好使用(Get-Date).ToUniversalTime(),或者在PSv5+ 中使用[datetime]::UtcNowMeasure-Command 使用更准确的计时,尽管这在实践中可能很少有关系。请注意,您可以直接减去 2 个[datetime] 实例以获得[timespan] 实例($finishTime - $startTime)——不需要New-TimeSpan。警告System.Diagnostics.Process.StartTime:它不适用于 PowerShell Core 中的类 Unix 平台并返回 $null
    【解决方案3】:

    引用:

    “启动-进程-等待-PassThru $proc $args”

    应该是:

    $buildtime = measure-command -expression {$results = "Start-Process -Wait -PassThru $proc $args"}

    '@ & -quit -batchmode "-logFile $logfile" -buildWindows64Player "$($fullpath)\Project.exe"@'

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多