Skip to main content

This article explains how to invoke a UNIX command from within COBOL using CALL “SYSTEM” and retrieve the return code of the command.

Problem:

Micro Focus Server Express and Visual COBOL provide a way for UNIX commands to be invoked from within COBOL. This is done by calling a routine named SYSTEM. Individual UNIX commands such as "ls" or "pwd" can be invoked, or entire shell scripts or executable programs can be invoked. How can the return code from the UNIX command be retrieved and communicated back to COBOL?

Resolution:

Here is an example of the COBOL syntax for invoking a UNIX command:


CALL "SYSTEM" USING CMD-LINE.

"SYSTEM" (uppercase) preserves the COBOL screen and state of the terminal, then calls the UNIX system() routine (lowercase), then afterwards restores the COBOL screen and terminal. For more information about "system", see the system(3) man page. It is possible to call "system" (lowercase) from COBOL, but then the COBOL screen would not be preserved, and might be corrupted.

To pass data back to COBOL from a UNIX command, have the command write the data to a file, then have COBOL read the file. For example, CALL "SYSTEM" with the command "ls -l >lsout.txt", then write COBOL code that opens "lsout.txt" as a line sequential file and reads the file line by line.

COBOL can retrieve the return code from the UNIX command. However, this is not simply the RETURN-CODE special register, or the optional RETURNING clause of the CALL statement; instead it is embedded in an individual byte within these values, and must be extracted. Here is example code:


--------- file "test.sh" --------
echo "Now executing test.sh"
echo "enter return code"
read rtncode
exit $rtncode

--------- file "callsys.cbl" -------
000001 working-storage section.
000002 01 return-code-ws pic s9(8) comp-5.
000003 01 filler redefines return-code-ws.
000004 $if little-endian defined
000005 05 filler pic x.
000006 05 high-order-byte pic s9 comp-5.
000007 05 filler pic x.
000008 05 low-order-byte pic s9 comp-5.
000009 $else
000010 05 filler pic xx.
000011 05 high-order-byte pic s9 comp-5.
000012 05 low-order-byte pic s9 comp-5.
000013 $end
000014 01 null-terminated-command.
000015 05 command pic x(2048).
000016 05 filler pic x value x"00".
000017 01 done-flag pic x value "n".
000018 88 done value "y".
000019 procedure division.
000020 perform get-user-input
000021 perform until done
000022 call "SYSTEM" using null-terminated-command
000023 returning return-code-ws
000024 if low-order-byte = 0
000025 display "return code from shell is: ",
000026 high-order-byte
000027 end-if
000028 perform get-user-input
000029 end-perform.
000030 stop run.
000031
000032 get-user-input.
000033 display spaces
000034 display "Enter command to be executed by shell"
000035 display " (enter 'done' to quit)"
000036 accept command
000037 if command = "done" move "y" to done-flag.

The program asks the user to enter a command. Then it calls "SYSTEM" using this command, and displays the return code. If you enter a fundamental command like "ls -l", you will see the command's output and a return code of 000. If you try "exit 6", COBOL will report that the shell exited with return code 006.

Try the command "sh test.sh" (which runs the example script above). The script prompts for a return code. Enter an integer between 0 and 127. The script will exit with that value, then the COBOL program will display it. This demonstrates that COBOL can get the return code from a script.

The return code is affected by the byte order on the platform. AIX, Solaris, HP/UX PARISC and HP/UX Itanium are "big-endian". HP/Tru64, Linux, SCO, and UnixWare are "little-endian". To handle these differences, this program uses "conditional compilation" with $if, $else, and $end statements (similar to #ifdef statements in the C preprocessor). When compiling this program on a little-endian platform, define a constant like this:


cob -C 'constant little-endian "yes"' callsys.cbl

Note that single quotes enclose the entire compile option, and double quotes surround the "yes" value.

As an alternative, insert a 78-level item in working-storage prior to the $if:


78 little-endian value "yes".

For big-endian platforms, no special options are necessary -- you can compile the program like this:


cob callsys.cbl

The program has been tested successfully using Object COBOL Developer Suite version 4.x, Server Express 2.x, 4.x, and 5.x, and also Visual COBOL 2.1, 2.2, and 2.3, on big-endian and little-endian platforms.

The program is based on macros in the system header file <sys/wait.h>. See the wait(2) man page for more information, that is, type "man 2 wait". A call to system() actually involves two return codes: one from the invoked command and one from the system() routine itself. The conventional UNIX behavior, when system() completes normally, is to embed the command's return code in the high-order bytes of the return code from system(). By studying the macros in <sys/wait.h>, a person could write code so COBOL could additionally recognize occasions when the command was terminated by a signal, and if so exactly which signal was received.

 

Old KB# 14406