Do I REALLY have to use $host.SetShouldExit?

I’ve written a PS script to submit a SAS job. Â This script will be used for both interactive processing (from the console) as well as batch processing (from JAMS).

In both scenarios, it’s critical that the correct error code returned from SAS is passed back to the calling process.

The last few lines from my script are:

END {
#=== This section is called once, at the end of the script ===#

# get the maximum return code
$global:LASTEXITCODE=($rtncodes | measure-object -max).maximum
}

However, when I submit this code from JAMS, it doesn’t send the correct return code to JAMS. Â Even jobs that end in error have rc=0.

If I change the code to:

END {
#=== This section is called once, at the end of the script ===#

# get the maximum return code
$global:LASTEXITCODE=($rtncodes | measure-object -max).maximum
$host.SetShouldExit($LASTEXITCODE)
}

Then the script works properly in JAMS, but blows away my PS console window when I run it interactively.

Any ideas for solving this conundrum?

Thanks,

Scott

PowerShell doesn’t much use stderr or stdout or any other decades-old console conveniences like that. You’re doing it the right way - but what it does, obviously, closes the console session if you’re doing it interactively. I suppose you could put some logic in to test and see if you’re running interactively, and do something different?

Hi Don,

Thanks for the reply. Â Much appreciated.

I’ve worked this one out, at least for my environment. The solution is somewhat JAMS specific, but I thought I’d share for the sake of completeness to this thread. Â Perhaps someone Googling for this same issue someday will hit this thread.

First of all, JAMS has a custom Powershell host which it uses to launch Powershell jobs. It also uses a concept called an execution method to determine how to launch the job. Â Since my job is just a Powershell script invoking SAS, I’ve cloned the standard Powershell execution method for my SAS jobs. Â I’ve done this instead of using the default Powershell execution method since I have to do some return code fiddling so that a SAS warning (non-zero return code) is not treated as an error and stop the downstream jobs from executing.

Here is some output from a simple JAMS job echoing the $host object:

Source Code:

$host | Format-List *

if ($host.Name -eq "JAMSHost") {
"This job <<JAMS.Jobname>> is running under JAMS."
}

And the Output in the JAMS log:

Name : JAMSHost
Version : 4.3.1.0
InstanceId : ab689a2e-3b18-42b9-92e0-d19826aedaad
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : en-AU
CurrentUICulture : en-US
PrivateData : MVPSI.JAMS.Host.PowerShell.JAMSHostInfo
IsRunspacePushed : 
Runspace :

This job _WriteVariables is running under JAMS.

So, in my script, I could do this check on the $host.Name property, and conditionally execute $host.SetShouldExit($lastexitcode) for JAMS submitted jobs only.

However, a JAMS execution method also supports the concept of a template.  A template is a “wrapper” around the job’s source code (a bit more than that really, but this explanation will do).

So, my JAMS execution method template is:

<<JAMS.Source>>

# echo the SAS exit code to the JAMS log, and pass this code back to JAMS

"LASTEXITCODE=$lastexitcode"
$host.SetShouldExit($lastexitcode)

### END OF FILE - do not add anything below this line ###

This essentially appends the $host.SetShouldExit($lastexitcode) statement to the end of the job source. Â This returns the correct errorcode created by my Powershell script back to the JAMS scheduler.

Details:

Here is what I think is happening:

  1. JAMS creates a temporary file from the template. Â It expands JAMS tokens, such as <<JAMS.Source>> within the template. Â It appends the $host.SetShouldExit statement to the end of the source code.

  2. It launches the temporary script using its custom Powershell host.

  3. When it encounters my script invocation, it starts another process to run the script (???)

  4. When my script invokes SAS via $p=Start-Process … -ArgumentList -Wait -NoNewWindow -Passthru, it starts another process to run the script.

  5. I use $script:sasrc=$p.ExitCode to capture the exit code from SAS

  6. I set $global:lastexitcode=$script:sasrc at the end of my script (I’m not sure this is needed??? But it works.)

  7. This sets $lastexitcode within the JAMS custom host, after my script has ended.

  8. Finally, $host.SetShouldExit($lastexitcode) returns the exit code to the scheduler.

I think that’s the sequence of events…