Reputation:
I have a script that runs the following commands:
$result = Start-Process -Wait -PassThru $proc $args
I want to measure how long it takes. Based on this article, I am trying the following:
$buildtime = measure-command -expression { $result = Start-Process -Wait -PassThru $proc $args }
Unfortunately, it throws the following error:
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
Due to other issues I had with strings and variable expansion, $args
is defined like this:
$args = @"
& -quit -batchmode -logFile "$logfile" -buildWindows64Player "$($fullpath)\Project.exe"
"@
How can I fix this? Or is there another way to capture the runtime of the script? I'm trying to learn PS so I'd rather avoid just capturing the time before I run it and after I run and comparing, but if that's my only option I will.
Upvotes: 2
Views: 1181
Reputation: 440317
stivix's answer contains the crucial pointer - $args
is an automatic variable that shouldn't be used as a custom variable - but it's worth digging deeper:
$args
is automatically set to the array ([object[]]
) of positional arguments passed to a simple script [block] or function[1], inside that code unit; if no arguments are passed, $args
contains an empty array.
Since you cannot pass arguments intended to be seen by a script block passed to Measure-Command
's -Expression
parameter, $args
is by definition the empty array inside that script block, which caused Start-Process
to fail.
As an aside: That Start-Process
fails with an empty array - instead of interpreting it as "no arguments to pass" - is a problem in Windows PowerShell that has since been corrected in PowerShell Core.
That PowerShell even lets you assign a value to $args
is problematic.
Unfortunately, this is part of a larger problem of not preventing custom use of automatic variables - see this answer.
The insidious aspect of this behavior is that the problem doesn't surface until you enter a script [block] or function; in the caller's scope the custom value assigned to $args
does take effect, as the following example shows:
$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)" }
The above yields:
after assignment: [hi]
in script block: []; element count: 0
The 1st output line shows that the assignment in the caller's scope worked as expected.
The 2nd output line shows that $args
was automatically set to an empty array upon entering the script block, because no arguments were passed to the script block ({ ... }
).
Note: Technically, a local $args
variable is created inside the script block, and the caller's copy is still accessible if you access the caller's scope explicitly (e.g. via (Get-Variable args -Scope 1 -ValueOnly)
), but the more sensible approach is to avoid custom use of $args
altogether.
[1] $args
only contains those arguments that aren't bound to explicitly declared parameters, if any. In advanced script or functions, you may only ever pass arguments to explicitly defined parameters, so $args
is not defined there, because its use would be pointless; in fact, in an advanced script / function $args
does retain a caller's custom $args
definition if run in the same scope domain, but that shouldn't be relied upon.
Upvotes: 2
Reputation: 31
Edit: I just checked, your $args variable is empty, turns out $args is a built-in variable. Once I changed variable name, it's not null anymore, try that.
How about you try one of these approaches:
1.
$startTime = Get-Date
$processToRun = Start-Process notepad.exe -PassThru -Wait
$finishTime = Get-Date
$timeDifference = New-TimeSpan -Start $startTime -End $finishTime
Upvotes: 3
Reputation: 1
To put quotes:
"Start-Process -Wait -PassThru $proc $args"
should be:
$buildtime = measure-command -expression {$results = "Start-Process -Wait -PassThru $proc $args"}
and
'@ & -quit -batchmode "-logFile $logfile" -buildWindows64Player "$($fullpath)\Project.exe"@'
Upvotes: -1