Rexx SHELL Returns Exit Status, Not Output — Here's How to Use It
The Rexx SHELL function returns a command's exit status while the text output lands in SHELLOUTPUT. Treat them as two separate results, validate input, and watch platform quoting.
04 Dec 2025, 15:05 UTC

You call SHELL('ls -l /tmp') in a Rexx script, expecting the directory listing back. What you get is a number — the command's exit status. The listing is sitting in a global variable called SHELLOUTPUT. That split is the whole trick to using SHELL well, and it is also where most first attempts go wrong.
This is a blog about a specific engineering decision: when to reach for SHELL, how to read its two results, and what it will not do for you.
What SHELL actually returns
SHELL is a Rexx function that runs an external operating-system command. Its return value is the command's exit status — typically 0 for success and a non-zero number for failure. The text the command wrote to standard output is placed in SHELLOUTPUT, a global variable that the function sets.
Two consequences follow immediately:
- A non-zero status is not an exception. Rexx will not stop your script for you. You must test the status yourself.
- Reading
SHELLOUTPUTwithout checking the status can make a failed command look like an empty successful one.
Availability is not universal. SHELL appears in mainstream IBM-derived Rexx implementations and in some others, but it is an extension rather than part of the ANSI Rexx standard. Embedded or minimal interpreters may not provide it. Check your interpreter's documentation before you build on it.
A worked example: list a directory and fail loudly
The pattern below runs on a Unix-like system with a Rexx interpreter that supports SHELL. Run it as the same user who would normally run the command; no special privileges are required for reading a directory you can already read.
/* list a directory, stop on failure */
target = '/tmp/reports'
cmd = 'ls -l' target
status = SHELL(cmd)
IF status \= 0 THEN DO
SAY 'Command failed. Status:' status
SAY 'Output:' SHELLOUTPUT
EXIT status
END
SAY 'Listing follows:'
SAY SHELLOUTPUT
What to check: the script prints the listing and exits with status 0 when the directory exists and is readable. If you change target to a path that does not exist, ls returns a non-zero status, the script prints the error text from SHELLOUTPUT, and exits with that same non-zero status.
That last step matters. Passing the status through with EXIT status lets a calling script or scheduler see the failure instead of a silent success.
Platform differences are not cosmetic
On Unix-like systems, SHELL typically hands the string to /bin/sh. On Windows, it typically uses cmd.exe. The command syntax, quoting rules, and available utilities differ between those shells.
| Concern | Unix-like (/bin/sh) | Windows (cmd.exe) |
|---|---|---|
| Path separator | / | \ (forward slashes often work but are not guaranteed) |
| Quoting a path with spaces | Single or double quotes | Double quotes; single quotes are literal |
| Exit status of a missing command | Usually 127 | Usually 1 or 9009 depending on context |
Do not assume a command string that works on one platform will work on the other. If your script must be portable, keep the command simple, avoid shell-specific syntax, and test on both targets.
Trade-offs and limitations
Synchronous execution. SHELL waits for the command to finish. A long-running utility will block your Rexx script for its full duration. If you need concurrency, SHELL is the wrong tool.
Output lives in memory. SHELLOUTPUT is a single string. A command that produces megabytes of output can put pressure on memory. For large streams, redirect the command's output to a file and read the file in Rexx instead.
Command injection. If any part of the command string comes from user input, an attacker can append shell metacharacters and run arbitrary commands. Validate input against a strict allowlist, or avoid building the command from external data at all. This is a real risk, not a theoretical one.
Portability. Because SHELL is an extension, a script that depends on it may not run on every Rexx interpreter. If portability is a requirement, isolate the call behind a small wrapper and document the dependency.
How to verify your setup
Run a minimal script in your target interpreter:
result = SHELL('echo hello')
SAY 'Exit:' result
SAY 'Output:' SHELLOUTPUT
Expected: the output line contains hello and the exit status is 0. Then run the same script on a Windows host and confirm that cmd.exe is invoked and the output is captured. Finally, run a command that fails — for example false on Unix or exit /b 1 on Windows — and confirm that the status is non-zero and no exception is raised.
If any of those checks fail, your interpreter may not support SHELL, or its behavior may differ from the description here. Consult its documentation before relying on the function in production.
The practical takeaway: treat the exit status and SHELLOUTPUT as two separate results, check the status before you trust the output, and keep the command string under your control.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.