1

I need to execute a program (.exe) from a powershell script multiple times and limit the execution time to 20 min for each instance - after that I want to abandon the program(exit execution) and report an error. Currently the code below uses the Invoke-Expression statement to run the program with parameters and pass the exit code to $key.

foreach($acct in $accts)

# run batch

{
$Startdate = get-date
$cmd = $prgPath + $acct.clientID + ' ' +  '{0:yyyyMMdd}' -f $rundate  + ' ' + '{0:yyyyMMdd}' -f $rundate  + ' E'
$key = $cmd  | Invoke-Expression
$ac = $acct.clientID
$enddate = Get-Date
$insertSQL = "insert into tbluserkeys values('$key', '$ac','$Startdate','$enddate')"

# insert key 

Invoke-Sqlcmd -ServerInstance 'dbsvr' -Database 'websb' -Query $insertSQL

}

For the runtime limit I want to use a thread job to start the program in a separate thread - I tried the code below - but when I check the status I see that PS takes the literal string and not command 3 Job3 ThreadJob Completed True PowerShell $key = $cmd | Invoke-...

  • how do I pass the command and retrieve the exit code similar to the invoke-expression statement? Also - should I use Get-process to check for the image and track the execution time or or is there another way to restrict the duration of the running process? It is important that the running job is killed to free system resources.

Thanks for any advice!

$Startdate = get-date
$cmd = $prgPath + $acct.clientID + ' ' +  '{0:yyyyMMdd}' -f $rundate  + ' ' + '{0:yyyyMMdd}' -f $rundate  + ' E'
$ac = $acct.clientID

# execute program

Start-ThreadJob -ScriptBlock {$key = $cmd  | Invoke-Expression}

# check if program run time exceeds maximum execution time

$process = Get-Process | Where-Object {$_.ProcessName -eq 'AS400SCRIPT'}

while($process -ne $null)

1 Answers1

3
  • Avoid using Invoke-Expression in general, and in particular for invoking commands (see this answer), even if you need to construct their arguments programmatically - use argument splatting instead.

  • Use the $using: scope in the thread-job script block to refer to values from the caller's scope.

  • Use Wait-Job with its -Timeout parameter to wait for a job to finish within the specified period.

  • Use Receive-Job to collect the output emitted by the job.

    • Note: Receive-Job reports the content of the job's output streams, but has no concept of an exit code.

    • If you want to report the process exit code of an external program that ran in the job script block, output $LASTEXITCODE from there, which, however, requires you to separate that value from the program's output itself.

    • Generally, it would be useful if a thread / background job automatically provided by a way to query the value of $LASTEXITCODE via the job's properties - GitHub issue #5422 requests just that.

A simplified example:

# Sample object
$acct = [pscustomobject] @{ clientID = 'foo' }

# Define the name / path of the program to invoke.
# 'echo' is used as a stand-in for your program.
$prgPath = 'echo'
# Define its arguments as an *array*.
# Note: If you were calling a PowerShell command instead, it's
#       better to use a *hashtable*. See about_Splatting.
$prgArgs = @(
  $acct.clientID,
  ('{0:yyyyMMdd}' -f $rundate)
  ('{0:yyyyMMdd}' -f $rundate),
  'E'
) 

# Start the thread job.
# Use the $using: scope to refer to the values of variables in the 
# caller's scope.
# This sample script block runs the given command then  sleeps
# for a day, provoking a timeout below.
$jb = Start-ThreadJob { & $using:prgPath @using:prgArgs; Start-Sleep 86400 }

# Wait for the job to finish with a given timeout (in seconds).
# Note that receiving *no output* implies that waiting timed out.
# Applying -not to no output is equivalent to -not $false and therefore $true.
$timedOut = -not ($jb | Wait-Job -Timeout 2)

if ($timedOut) { 
  Write-Warning "Terminating job due to timeout."
  $jb | Stop-Job
}

# Collect all job output and remove the job.
$outputReceived = $jb | Receive-Job -Wait -AutoRemoveJob

# Show results
[pscustomobject] @{
  Job = $jb.Command
  TimedOut = $timedOut
  JobOutput = $outputReceived
}
mklement0
  • 382,024
  • 64
  • 607
  • 775