Intermediate

I/O Operations in REXX

Learn console and file input/output in REXX - SAY, PULL, PARSE, and the stream functions LINEIN, LINEOUT, and STREAM using Regina REXX

Input and output are where REXX’s origins as a scripting and text-processing language really show. Because everything in REXX is a string, reading a line from the keyboard, writing a record to a file, and printing a report are all just string operations—there are no readers, writers, buffers, or type conversions to manage.

REXX handles I/O at two levels. Console I/O uses the plain-English instructions you already met in Hello World: SAY for output and PULL (or PARSE PULL) for input. File I/O uses a small set of built-in functions—LINEIN, LINEOUT, LINES, and STREAM—that treat any file as a named stream of lines or characters.

This tutorial builds on the SAY instruction from the Hello World page and shows how to read from the console, format output into columns, and read and write files. All examples run unchanged under Regina REXX in Docker.

Formatted Console Output

SAY does more than print a single string. Because REXX concatenates values freely, you can build up formatted lines using adjacency, the || operator, and alignment functions like LEFT, RIGHT, and FORMAT.

Create a file named formatted_output.rexx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* Formatted console output in REXX */

/* Concatenation: adjacency inserts one blank, || joins directly */
first = "Grace"
last  = "Hopper"
say first last            /* separated by a single blank */
say first || last         /* joined with no space */
say first", "last         /* literal text between the values */

/* Aligning columns with LEFT and RIGHT */
say left("Name", 10) || right("Score", 6)
say left("Ada", 10)  || right(95, 6)
say left("Alan", 10) || right(88, 6)

/* Numeric formatting with FORMAT */
say format(3.14159, 3, 2)   /* 3 digits before, 2 after the point */
say format(1000, 6)         /* right-align in a 6-character field */

Here LEFT(string, n) pads on the right to width n, RIGHT(string, n) pads on the left, and FORMAT(number, before, after) controls the integer and decimal widths of a number. Combining them produces neatly aligned columns without any external library.

Reading Input from the Console

The PULL instruction reads a line of text from the console (standard input). A closely related form, PARSE PULL, does the same thing but preserves the original case—plain PULL translates the input to uppercase.

Create a file named greet.rexx:

1
2
3
4
/* Reading input from the console */
say "What is your name?"
parse pull answer
say "Nice to meet you," answer || "!"

PARSE PULL answer reads the whole line into the variable answer with its case intact. If you used plain PULL answer instead, typing Ada would store ADA.

Parsing Multiple Values

REXX’s PARSE is a powerful template engine. When you list several variables, REXX splits the input into words and assigns them in order—perfect for reading multiple values from a single line.

Create a file named add_numbers.rexx:

1
2
3
4
/* Parse multiple values from one input line */
say "Enter two numbers separated by a space:"
parse pull x y
say x "+" y "=" x + y

PARSE PULL x y splits the line on blanks: the first word goes to x, the rest to y. Because REXX is typeless, the strings "12" and "30" are used directly in arithmetic—x + y evaluates them as numbers.

Reading and Writing Files

File I/O in REXX treats a filename as a stream. You don’t get a file handle back—you simply name the file in each call:

  • LINEOUT(name, string) — write string as a line to the file name
  • LINEIN(name) — read and return the next line from name
  • LINES(name) — return a non-zero value while lines remain to be read
  • STREAM(name, 'C', command) — send a control command such as OPEN, CLOSE, or OPEN WRITE REPLACE

Because these are functions, you invoke them with CALL when you don’t need the return value.

Create a file named io_operations.rexx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* File I/O in REXX: write records, then read them back */

datafile = "inventory.txt"

/* Open for writing, replacing any existing content */
call stream datafile, "c", "open write replace"

call lineout datafile, "Widgets,42"
call lineout datafile, "Gadgets,17"
call lineout datafile, "Gizmos,8"

call stream datafile, "c", "close"
say "Wrote 3 records to" datafile

/* Reopen for reading and process each line */
call stream datafile, "c", "open read"

total = 0
say "Inventory:"
do while lines(datafile) > 0
    parse value linein(datafile) with name "," quantity
    say "  " || left(name, 10) || right(quantity, 4)
    total = total + quantity
end

call stream datafile, "c", "close"
say "Total items:" total

A few things worth noting:

  • OPEN WRITE REPLACE truncates the file first, so re-running the script gives the same result every time instead of appending.
  • PARSE VALUE ... WITH name "," quantity splits each line on the literal comma—name gets everything before it, quantity everything after. This is REXX’s idiomatic way to parse delimited data.
  • DO WHILE lines(datafile) > 0 loops until the stream reaches end-of-file.
  • Always close a stream with STREAM(..., 'C', 'CLOSE') (or CALL LINEOUT name with no data) to flush and release it.

Running with Docker

Run each example with the official Regina REXX image. The -i flag on the input examples keeps standard input open so PARSE PULL can read the piped text.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Pull the Regina REXX image
docker pull rzuckerm/rexx:3.6-5.00-1

# Formatted output (no input needed)
docker run --rm -v $(pwd):/app -w /app rzuckerm/rexx:3.6-5.00-1 rexx /app/formatted_output.rexx

# Console input - pipe a name into the program
echo "Ada" | docker run --rm -i -v $(pwd):/app -w /app rzuckerm/rexx:3.6-5.00-1 rexx /app/greet.rexx

# Multiple values - pipe two numbers
echo "12 30" | docker run --rm -i -v $(pwd):/app -w /app rzuckerm/rexx:3.6-5.00-1 rexx /app/add_numbers.rexx

# File read/write (creates inventory.txt in the mounted directory)
docker run --rm -v $(pwd):/app -w /app rzuckerm/rexx:3.6-5.00-1 rexx /app/io_operations.rexx

Expected Output

Running formatted_output.rexx:

Grace Hopper
GraceHopper
Grace, Hopper
Name       Score
Ada           95
Alan          88
  3.14
  1000

Running greet.rexx with the piped input Ada:

What is your name?
Nice to meet you, Ada!

Running add_numbers.rexx with the piped input 12 30:

Enter two numbers separated by a space:
12 + 30 = 42

Running io_operations.rexx:

Wrote 3 records to inventory.txt
Inventory:
  Widgets     42
  Gadgets     17
  Gizmos       8
Total items: 67

Key Concepts

  • SAY for output, PULL/PARSE PULL for input — console I/O uses plain-English instructions with no formatting boilerplate. Use PARSE PULL to preserve the original case of the input.
  • Everything is a string — input arrives as text and is used directly in arithmetic or comparisons; there is no separate parsing or type-conversion step.
  • PARSE templates — listing variables splits on blanks, while literal patterns like "," split on delimiters, making REXX exceptional at chewing through structured text.
  • Files are named streamsLINEIN, LINEOUT, and LINES operate on a filename directly; there is no file-handle object to track.
  • Control streams with STREAM(name, 'C', command)OPEN WRITE REPLACE, OPEN READ, and CLOSE give you deterministic, repeatable file behavior.
  • Alignment functionsLEFT, RIGHT, and FORMAT produce columnar reports and formatted numbers without any external dependencies.
  • Always close your streams — closing flushes buffered output and releases the file so it can be reopened cleanly.

Running Today

All examples can be run using Docker:

docker pull rzuckerm/rexx:3.6-5.00-1
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining