20

I have the following job in powershell:

$job = start-job {
  ...
  c:\utils\MyToolReturningSomeExitCode.cmd
} -ArgumentList $JobFile

How do I access the exit code returned by c:\utils\MyToolReturningSomeExitCode.cmd ? I have tried several options, but the only one I could find that works is this:

$job = start-job {
  ...
  c:\utils\MyToolReturningSomeExitCode.cmd
  $LASTEXITCODE
} -ArgumentList $JobFile

...

# collect the output
$exitCode = $job | Wait-Job | Receive-Job -ErrorAction SilentlyContinue
# output all, except the last line
$exitCode[0..($exitCode.Length - 2)]
# the last line is the exit code
exit $exitCode[-1]

I find this approach too wry to my delicate taste. Can anyone suggest a nicer solution?

Important, I have read in the documentation that powershell must be run as administrator in order for the job related remoting stuff to work. I cannot run it as administrator, hence -ErrorAction SilentlyContinue. So, I am looking for solutions not requiring admin privileges.

Thanks.

mark
  • 59,016
  • 79
  • 296
  • 580

2 Answers2

14

If all you need is to do something in background while the main script does something else then PowerShell class is enough (and it is normally faster). Besides it allows passing in a live object in order to return something in addition to output via parameters.

$code = @{}

$job = [PowerShell]::Create().AddScript({
  param($JobFile, $Result)
  cmd /c exit 42
  $Result.Value = $LASTEXITCODE
  'some output'
}).AddArgument($JobFile).AddArgument($code)

# start thee job
$async = $job.BeginInvoke()

# do some other work while $job is working
#.....

# end the job, get results
$job.EndInvoke($async)

# the exit code is $code.Value
"Code = $($code.Value)"

UPDATE

The original code was with [ref] object. It works in PS V3 CTP2 but does not work in V2. So I corrected it, we can use other objects instead, a hashtable, for example, in order to return some data via parameters.

Roman Kuzmin
  • 40,627
  • 11
  • 95
  • 117
  • 1
    I am using more job related API, like `Receive-Job` to get the job output until now, $job.State to check whether the job is running or `Wait-Job` with timeout to let the job run for a few seconds (before polling its output). How do you do it with the `PowerShell` object? – mark Dec 26 '11 at 14:24
  • 1
    Did you take a look at the documentation of the class (I provided the link)? `Receive-Job` is done by `EndInvoke`. `Wait-Job` is also done by `EndInvoke`. Timeout control is also possible via `$async` object. – Roman Kuzmin Dec 26 '11 at 15:12
  • 1
    `$async.AsyncWaitHandle.WaitOne()` is for waiting with some timeout specified. – Roman Kuzmin Dec 26 '11 at 15:15
  • I feel, this approach is giving more control, especially displaying something inside job, and passing parameters to script block and returning codes etc. Not sure if Start-Job does similar things... – Farrukh Waheed Oct 04 '13 at 05:37
11

One way you can detect if the background job failed or not based on an exit code is to evaluate the exit code inside the background job itself and throw an exception if the exit code indicates an error occurred. For instance, consider the following example:

$job = start-job {
    # ...
    $output = & C:\utils\MyToolReturningSomeExitCode.cmd 2>&1
    if ($LASTEXITCODE -ne 0) {
        throw "Job failed. The error was: {0}." -f ([string] $output)
    }
} -ArgumentList $JobFile

$myJob = Start-Job -ScriptBlock $job | Wait-Job 
if ($myJob.State -eq 'Failed') {
    Receive-Job -Job $myJob
}

A couple things of note in this example. I am redirecting the standard error output stream to the standard output stream to capture all textual output from the batch script and returning it if the exit code is non-zero indicating it failed to run. By throwing an exception this way the background job object State property will let us know the result of the job.

Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
  • 1
    throwing exception may break execution which might not be a good solution. As in my case, I would like to evaluate exit code and do some decisions accordingly, e.g. logging errors in files, showing status on screen etc. – Farrukh Waheed Oct 04 '13 at 05:39