Exit with error code from SPSS syntax - spss

I am using a batch file which calls an SPSS production job which runs many syntax files.
In the syntax files I want to able to check some variables, and if certain conditions are not met then I want to stop the production job, exit SPSS and return an error code to the batch file.
The batch file needs to stop running the next commands based on the error code returned. I know how to do this in the batch file already.
The most basic solution could be if the error code is not 0 then stop, and the error text will be output to a separate text file from within the syntax. A bonus would be a different error code which I could then match to where in the syntax that code is thrown.
What is the best way to achieve this in the SPSS syntax and or production file?

One way to do this would be to execute Statistics as an external mode Python job. Then you could interrogate any results, catch exceptions, and set exit codes and messages however, you like. Here is an example:
jobs.py:
Python jobs
import sys
sys.path.append(r"""c:/spss23/python/lib/site-packages""")
import spss
try:
spss.Submit("""INSERT FILE="c:/temp/syntax1.sps".""")
except:
print "syntax1.spss failed"
exit(code=1)
try:
spss.Submit("""INSERT FILE="c:/temp/syntax2.sps".""")
except:
print "syntax1.spss failed"
exit(code=2)
Then the bat file would do
python c:/myjobs/jobs.py
print %ERRORLEVEL%
or similar. The job would need to save the output in appropriate format using OMS or shell redirection. (The blocks after try and except should be indented.)

In external mode, you could use code like this or you could interrogate items in the Viewer.
import spss, spssdata
curs = spssdata.Spssdata("variable2")
for case in curs:
if case[0] == 6:
exit(99)
curs.CClose

Related

Getting `spss.Submit` to log commands into the `output` window?

When I execute spss syntax commands from a .sps script, each command is written to the output window before it executes giving me a clear log of exactly how an output was created.
Even if the command is an INSERT command executing a different script - I get a log of the commands from that script.
This is very useful for many reasons:
sanity checking - I can always see exactly what went in to creating a specific output (which filters I used, etc.)
recreation - I (or someone else with this output) can easily re-run the same commands because they're right there.
debugging - if there's an error, I can see which commands caused it
However, when I run commands using spss.Submit inside a python block (in a BEGIN PROGRAM-END PROGRAM block), the actual commands called aren't logged into the output window.
I know I can find a full log in a log file - but that's not helpful.
Is there a way to tell spss to continue to log all the commands in the output window?
You can use set mprint on. before the begin program statement to have the syntax that is run via spss.Submit()show up in the output window. I like simpy putting it on the very top of my syntax file as a "set it and forget it".
For example like so:
set mprint on.
begin program python3.
import spss
vars = list(range(1,11))
for var in vars:
spss.Submit(f'compute v{var} = 0. ')
end program.

How do I command the Rascal Shell not to truncate my output if it's quite long

This seems like a pretty basic question to me, but I don't seem to be able to find the answer anywhere. I need to get the full output of a command in the Rascal shell, however, Rascal seems to truncate long outputs automatically. Is there any way to tell the shell to always show the full output for a given command.
Example:
rascal>calculateCodeDuplication("smallsql0.21_src")
list[list[loc]]: [
[
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1310,26,<34,0>,<34,26>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1494,56,<43,1>,<43,57>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1558,49,<46,1>,<48,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1616,44,<51,1>,<53,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1665,57,<55,1>,<57,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1731,51,<59,1>,<61,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1791,57,<63,1>,<66,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1853,65,<68,1>,<71,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1927,61,<74,1>,<77,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1993,56,<79,1>,<82,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2055,50,<84,1>,<86,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2114,54,<88,1>,<90,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2177,42,<92,1>,<94,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2248,7,<96,24>,<96,31>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2225,136,<96,1>,<101,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2390,8,<103,24>,<103,32>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2367,153,<103,1>,<107,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2526,54,<109,1>,<111,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2586,51,<113,1>,<115,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2671,16,<118,27>,<118,43>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2645,78,<118,1>,<120,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2731,49,<123,1>,<125,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2789,48,<128,1>,<130,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2846,48,<133,1>,<135,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2902,46,<138,1>,<140,2>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(2955,72,<143,1>,<143,73>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(3118,19,<149,43>,<149,62>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(3041,227,<146,4>,<152,5>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(1340,1931,<36,0>,<153,1>),
|java+compilationUnit:///src/smallsql/database/NoFromResult.java|(0,3273,<1,0>,<153,3>),
|java+compilationUnit:///src/smallsql/database/ExpressionFunctionLog.java|(1305,26,<33,0>,<33,26>),
|java+compilationUnit:///src/smallsql/database/ExpressionFunctionLog.java|(1418,51,<38,4>,<38,55>),
|java+compilationUnit:///src/smallsql/database/ExpressionFunctionLog.java|(1477,123,<40,4>,<43,5>),
|java+compilationUnit:///src/smallsql/database/ExpressionFunctionLog.java|(1337,266,<36,0>,<44,1>),
|java+compilationUnit:///src/smallsql/database/ExpressionFunctionLog.java|(0,1603,<1,0>,<44,1>),
|java+compilationUnit:///src/smallsql/junit/TestDBMetaData.java|(1298,23,<33,0>,<33,23>),
|java+compilationUnit:///src/smallsql/junit/TestDBMetaData.java|(1325,18,<35,...
(note that the console outputs cuts off in the middle of the line with ...)
There are a number of ways. Here are the two I use most frequently:
use iprintln or println from the IO module. The former does indentation which is nice for complex outputs. example: import IO; iprintln(calculateCodeDuplication("smallsql0.21_src")); The nice thing about this is that in the terminal values of loc type are hyperlinks
open an editor for the value using the util::ValueUI module (only in Eclipse), for example: import util::ValueUI; text(calculateCodeDuplication("smallsql0.21_src")); This will pop up an editor with the indented output value (nice to search in using CTRL+F, but the loc is not a hyperlink. Would love to have a contribution in that direction :-))
Caveat for the first one is that scrolling up and down in the Eclipse terminal is very slow on Mac's, and not really fast on Windows either.

Write to the system's standard error in Progress

I am writing a small program in Progress that needs to write an error message to the system's standard error. What ways, simple if at all possible, can I use to print to standard error?
I am using OpenEdge 11.3.
When on Windows (10.2B+) you can use .NET:
System.Console:Error:WriteLine ("This is an error message") .
together with
prowin32 2> stderr.out
Progress doesn't provide a way to write to stderr - the easiest way I can think of is to output-through an external program that takes stdin and echoes it to stderr.
You could look into LOG-MANAGER:WRITE-MESSAGE. It won't log to standard output or standard error, but to a client-specific log. This log should be monitored in any case (specifically if the client is an application server).
From the documentation:
For an interactive or batch client, the WRITE-MESSAGE( ) method writes the log entries to the log file specified by the LOGFILE-NAME attribute or the Client Logging (-clientlog) startup parameter. For WebSpeed agents and AppServer servers, the WRITE-MESSAGE() method writes the log entries to the server log file. For DataServers, the WRITE-MESSAGE() method writes the log entries to the log file specified by the DataServer Logging (-dslog) startup parameter.
LOG-MANAGER:WRITE-MESSAGE("Got here, x=" + STRING(x), "DEBUG1").
Will write this in the log:
[04/12/05#13:19:19.742-0500] P-003616 T-001984 1 4GL DEBUG1 Got here, x=5
There are quite a lot of options regarding the LOG-MANAGER system, what messages to display, where the file is placed, etc.
There is no easy way, but in Unixen you can always do something like this using OUTPUT THROUGH (untested):
output through "cat >&2" no-echo unbuffered.
Alternatively -- and this is tested -- if you just want error messages from a batch-mode program to go to standard out then
output through "tee" ...
...definitely works.

Simple program that reads and writes to a pipe

Although I am quite familiar with Tcl this is a beginner question. I would like to read and write from a pipe. I would like a solution in pure Tcl and not use a library like Expect. I copied an example from the tcl wiki but could not get it running.
My code is:
cd /tmp
catch {
console show
update
}
proc go {} {
puts "executing go"
set pipe [open "|cat" RDWR]
fconfigure $pipe -buffering line -blocking 0
fileevent $pipe readable [list piperead $pipe]
if {![eof $pipe]} {
puts $pipe "hello cat program!"
flush $pipe
set got [gets $pipe]
puts "result: $got"
}
}
go
The output is executing go\n result:, however I would expect that reading a value from the pipe would return the line that I have sent to the cat program.
What is my error?
--
EDIT:
I followed potrzebie's answer and got a small example working. That's enough to get me going. A quick workaround to test my setup was the following code (not a real solution but a quick fix for the moment).
cd /home/stephan/tmp
catch {
console show
update
}
puts "starting pipe"
set pipe [open "|cat" RDWR]
fconfigure $pipe -buffering line -blocking 0
after 10
puts $pipe "hello cat!"
flush $pipe
set got [gets $pipe]
puts "got from pipe: $got"
Writing to the pipe and flushing won't make the OS multitasking immediately leave your program and switch to the cat program. Try putting after 1000 between the puts and the gets command, and you'll see that you'll probably get the string back. cat has then been given some time slices and has had the chance to read it's input and write it's output.
You can't control when cat reads your input and writes it back, so you'll have to either use fileevent and enter the event loop to wait (or periodically call update), or periodically try reading from the stream. Or you can keep it in blocking mode, in which case gets will do the waiting for you. It will block until there's a line to read, but meanwhile no other events will be responded to. A GUI for example, will stop responding.
The example seem to be for Tk and meant to be run by wish, which enters the event loop automatically at the end of the script. Add the piperead procedure and either run the script with wish or add a vwait command to the end of the script and run it with tclsh.
PS: For line-buffered I/O to work for a pipe, both programs involved have to use it (or no buffering). Many programs (grep, sed, etc) use full buffering when they're not connected to a terminal. One way to prevent them to, is with the unbuffer program, which is part of Expect (you don't have to write an Expect script, it's a stand-alone program that just happens to be included with the Expect package).
set pipe [open "|[list unbuffer grep .]" {RDWR}]
I guess you're executing the code from http://wiki.tcl.tk/3846, the page entitled "Pipe vs Expect". You seem to have omitted the definition of the piperead proc, indeed, when I copy-and-pasted the code from your question, I got an error invalid command name "piperead". If you copy-and-paste the definition from the wiki, you should find that the code works. It certainly did for me.

Capturing output from WshShell.Exec using Windows Script Host

I wrote the following two functions, and call the second ("callAndWait") from JavaScript running inside Windows Script Host. My overall intent is to call one command line program from another. That is, I'm running the initial scripting using cscript, and then trying to run something else (Ant) from that script.
function readAllFromAny(oExec)
{
if (!oExec.StdOut.AtEndOfStream)
return oExec.StdOut.ReadLine();
if (!oExec.StdErr.AtEndOfStream)
return "STDERR: " + oExec.StdErr.ReadLine();
return -1;
}
// Execute a command line function....
function callAndWait(execStr) {
var oExec = WshShell.Exec(execStr);
while (oExec.Status == 0)
{
WScript.Sleep(100);
var output;
while ( (output = readAllFromAny(oExec)) != -1) {
WScript.StdOut.WriteLine(output);
}
}
}
Unfortunately, when I run my program, I don't get immediate feedback about what the called program is doing. Instead, the output seems to come in fits and starts, sometimes waiting until the original program has finished, and sometimes it appears to have deadlocked. What I really want to do is have the spawned process actually share the same StdOut as the calling process, but I don't see a way to do that. Just setting oExec.StdOut = WScript.StdOut doesn't work.
Is there an alternate way to spawn processes that will share the StdOut & StdErr of the launching process? I tried using "WshShell.Run(), but that gives me a "permission denied" error. That's problematic, because I don't want to have to tell my clients to change how their Windows environment is configured just to run my program.
What can I do?
You cannot read from StdErr and StdOut in the script engine in this way, as there is no non-blocking IO as Code Master Bob says. If the called process fills up the buffer (about 4KB) on StdErr while you are attempting to read from StdOut, or vice-versa, then you will deadlock/hang. You will starve while waiting for StdOut and it will block waiting for you to read from StdErr.
The practical solution is to redirect StdErr to StdOut like this:
sCommandLine = """c:\Path\To\prog.exe"" Argument1 argument2"
Dim oExec
Set oExec = WshShell.Exec("CMD /S /C "" " & sCommandLine & " 2>&1 """)
In other words, what gets passed to CreateProcess is this:
CMD /S /C " "c:\Path\To\prog.exe" Argument1 argument2 2>&1 "
This invokes CMD.EXE, which interprets the command line. /S /C invokes a special parsing rule so that the first and last quote are stripped off, and the remainder used as-is and executed by CMD.EXE. So CMD.EXE executes this:
"c:\Path\To\prog.exe" Argument1 argument2 2>&1
The incantation 2>&1 redirects prog.exe's StdErr to StdOut. CMD.EXE will propagate the exit code.
You can now succeed by reading from StdOut and ignoring StdErr.
The downside is that the StdErr and StdOut output get mixed together. As long as they are recognisable you can probably work with this.
Another technique which might help in this situation is to redirect the standard error stream of the command to accompany the standard output.
Do this by adding "%comspec% /c" to the front and "2>&1" to the end of the execStr string.
That is, change the command you run from:
zzz
to:
%comspec% /c zzz 2>&1
The "2>&1" is a redirect instruction which causes the StdErr output (file descriptor 2) to be written to the StdOut stream (file descriptor 1).
You need to include the "%comspec% /c" part because it is the command interpreter which understands about the command line redirect. See http://technet.microsoft.com/en-us/library/ee156605.aspx
Using "%comspec%" instead of "cmd" gives portability to a wider range of Windows versions.
If your command contains quoted string arguments, it may be tricky to get them right:
the specification for how cmd handles quotes after "/c" seems to be incomplete.
With this, your script needs only to read the StdOut stream, and will receive both standard output and standard error.
I used this with "net stop wuauserv", which writes to StdOut on success (if the service is running)
and StdErr on failure (if the service is already stopped).
First, your loop is broken in that it always tries to read from oExec.StdOut first. If there is no actual output then it will hang until there is. You wont see any StdErr output until StdOut.atEndOfStream becomes true (probably when the child terminates). Unfortunately, there is no concept of non-blocking I/O in the script engine. That means calling read and having it return immediately if there is no data in the buffer. Thus there is probably no way to get this loop to work as you want. Second, WShell.Run does not provide any properties or methods to access the standard I/O of the child process. It creates the child in a separate window, totally isolated from the parent except for the return code. However, if all you want is to be able to SEE the output from the child then this might be acceptable. You will also be able to interact with the child (input) but only through the new window (see SendKeys).
As for using ReadAll(), this would be even worse since it collects all the input from the stream before returning so you wouldn't see anything at all until the stream was closed. I have no idea why the example places the ReadAll in a loop which builds a string, a single if (!WScript.StdIn.AtEndOfStream) should be sufficient to avoid exceptions.
Another alternative might be to use the process creation methods in WMI. How standard I/O is handled is not clear and there doesn't appear to be any way to allocate specific streams as StdIn/Out/Err. The only hope would be that the child would inherit these from the parent but that's what you want, isn't it? (This comment based upon an idea and a little bit of research but no actual testing.)
Basically, the scripting system is not designed for complicated interprocess communication/synchronisation.
Note: Tests confirming the above were performed on Windows XP Sp2 using Script version 5.6. Reference to current (5.8) manuals suggests no change.
Yes, the Exec function seems to be broken when it comes to terminal output.
I have been using a similar function function ConsumeStd(e) {WScript.StdOut.Write(e.StdOut.ReadAll());WScript.StdErr.Write(e.StdErr.ReadAll());} that I call in a loop similar to yours. Not sure if checking for EOF and reading line by line is better or worse.
You might have hit the deadlock issue described on this Microsoft Support site.
One suggestion is to always read both from stdout and stderr.
You could change readAllFromAny to:
function readAllFromAny(oExec)
{
var output = "";
if (!oExec.StdOut.AtEndOfStream)
output = output + oExec.StdOut.ReadLine();
if (!oExec.StdErr.AtEndOfStream)
output = output + "STDERR: " + oExec.StdErr.ReadLine();
return output ? output : -1;
}

Resources