Q: I'm running QShell commands in my CL programs. How can I prevent the output from displaying on the screen? How can I tell whether an error occurred when the QShell command ran?
A: Starting in V5R1, there are environment variables that can be used to control the screen output and the error handling. You set them using the ADDENVVAR command as discussed below.
This CL program demonstrates the problem:
PGM
STRQSH CMD('ls /dirthatdoesnotexist')
ENDPGM
When you run that program, a screen will pop up in front of the user that shows an error message stating that the directory cannot be found. However, it's often better for QShell to alert your program, rather than the user, so that it can handle the error. If you set the QIBM_QSH_CMD_OUTPUT environment variable to "NONE," this screen will no longer appear.
PGM
/* TELL QSHELL NOT TO DISPLAY OUTPUT ON SCREEN */
RMVENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT)
MONMSG MSGID(CPFA981) /* ENVVAR DOES NOT EXIST */
ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE)
/* EXECUTE COMMAND -- WE WON'T KNOW IF IT FAILS, HOWEVER. */
STRQSH CMD('ls /dirthatdoesnotexist')
/* RESTORE DEFAULT BEHAVIOR. */
CHGENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(STDOUT)
ENDPGM
Although the user no longer sees the problem, the CL program still doesn't know that an error occurred. If we set the QIBM_QSH_CMD_ESCAPE_MSG variable to Y, an escape message will be sent to alert us that something failed.
PGM
/* IF VARIABLES WERE SET PREVIOUSLY, REMOVE THEM SO THAT OUR +
ADDENVVAR COMMANDS WORK. */
RMVENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT)
MONMSG MSGID(CPFA981) /* ENVVAR DOES NOT EXIST */
RMVENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG)
MONMSG MSGID(CPFA981) /* ENVVAR DOES NOT EXIST */
/* SET THE COMMAND OUTPUT TO NONE, AND TELL QSH TO ISSUE +
ESCAPE MESSAGES FOR ERRORS. */
ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE)
ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y)
/* EXECUTE OUR QSHELL COMMAND */
STRQSH CMD('ls /dirthatdoesnotexist')
MONMSG MSGID(QSH0005 QSH0006 QSH0007) EXEC(DO)
SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('QShell +
command failed!')
ENDDO
/* RESTORE DEFAULT BEHAVIOR */
CHGENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE)
CHGENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y)
ENDPGM
Now the program knows that the command failed, but it still doesn't know exactly what went wrong. We can get a bit more information if we retrieve the "exit status."
In QShell, each command that you run returns an exit status number ranging from 0 to 255. When this number is 0, the command was successful. When this number is 1 or more, it means that something went wrong. In the example above, the directory could not be found, so the LS command returned a 1 to the shell. When a command in QShell completes in this manner, the QShell will send you a QSH0005 escape message,and you can retrieve the exit status number by examining the first four bytes of the message data.
It's also possible for a QShell command to stop running because it was sent a signal from another process. If that should happen, the command cannot return an exit status because the command never completed. QShell will notify you of this by sending you a QSH0006 message, and you can retrieve the signal number from the first four bytes of message data.
There's one other way that a QShell command could end. If there's a bug in a QShell utility, it might crash, returning an escape message to the QShell. If that happens, QShell will send you a QSH0007 message to tell you about the failure.
This final sample program demonstrates retrieving the information from the three messages described above.
PGM
DCL VAR(&MSGID) TYPE(*CHAR) LEN(7)
DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(256)
DCL VAR(&RESULT) TYPE(*CHAR) LEN(4)
DCL VAR(&STATUS) TYPE(*DEC) LEN(10 0)
DCL VAR(&SIGNAL) TYPE(*DEC) LEN(10 0)
DCL VAR(&CHARSTAT) TYPE(*CHAR) LEN(10)
DCL VAR(&CHARSIG) TYPE(*CHAR) LEN(10)
/* IF VARIABLES WERE SET PREVIOUSLY, REMOVE THEM SO THAT OUR +
ADDENVVAR COMMANDS WORK. */
RMVENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT)
MONMSG MSGID(CPFA981) /* ENVVAR DOES NOT EXIST */
RMVENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG)
MONMSG MSGID(CPFA981) /* ENVVAR DOES NOT EXIST */
/* SET THE COMMAND OUTPUT TO NONE, AND TELL QSH TO ISSUE +
ESCAPE MESSAGES FOR ERRORS. */
ADDENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE)
ADDENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y)
/* EXECUTE OUR QSHELL COMMAND */
STRQSH CMD('ls /dirthatdoesnotexist')
/* HANDLE ERRORS. */
MONMSG MSGID(QSH0005 QSH0006 QSH0007) EXEC(DO)
RCVMSG MSGTYPE(*LAST) RMV(*YES) MSGDTA(&MSGDTA) +
MSGID(&MSGID)
/* CPF0005 - QSHELL COMMAND ENDED "NORMALLY." +
+
NOTE: THIS DOES *NOT* MEAN THAT THE COMMAND +
SUCCEEDED, BUT RATHER THAT THE COMMAND +
WAS NOT ENDED BY ANOTHER PROGRAM OR BY SOME +
KIND OF FAILURE. (THIS IS THE MOST COMMON +
MESSAGE) */
IF (&MSGID *EQ 'QSH0005') DO
CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4))
CHGVAR VAR(&STATUS) VALUE(%BIN(&RESULT))
CHGVAR VAR(&SIGNAL) VALUE(0)
ENDDO
/* QSH0006 - QSHELL COMMAND ENDED WHEN IT RECEIVED A +
SIGNAL. */
IF (&MSGID *EQ 'QSH0006') DO
CHGVAR VAR(&RESULT) VALUE(%SST(&MSGDTA 1 4))
CHGVAR VAR(&SIGNAL) VALUE(%BIN(&RESULT))
CHGVAR VAR(&STATUS) VALUE(-1)
ENDDO
/* QSH0007 - QSHELL COMMAND ENDED DUE TO AN EXCEPTION, +
FOR EXAMPLE THE PROGRAM CRASHED WITH A CPF MSG. */
IF (&MSGID *EQ 'QSH0007') DO
CHGVAR VAR(&STATUS) VALUE(-1)
CHGVAR VAR(&SIGNAL) VALUE(-1)
ENDDO
ENDDO
/* WHEN &STATUS IS NOT ZERO, WE KNOW OUR COMMAND FAILED. +
HERE WE CAN TRY TO HANDLE THE ERROR IF WE LIKE +
+
THE POSSIBLE VALUES ARE DIFFERENT FOR EACH QSHELL +
COMMAND, SO FOR THE SAKE OF DEMONSTRATION, I'LL JUST +
SEND AN ESCAPE MESSAGE. */
IF (&STATUS *NE 0) THEN(DO)
CHGVAR VAR(&CHARSTAT) VALUE(&STATUS)
CHGVAR VAR(&CHARSIG) VALUE(&SIGNAL)
SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('QShell +
command failed with status ' *CAT +
&CHARSTAT *CAT ' and signal ' *CAT &CHARSIG)
ENDDO
/* RESTORE DEFAULT BEHAVIOR */
CHGENVVAR ENVVAR(QIBM_QSH_CMD_OUTPUT) VALUE(NONE)
CHGENVVAR ENVVAR(QIBM_QSH_CMD_ESCAPE_MSG) VALUE(Y)
ENDPGM