Module 5: REXX EXECIO File I/O
REXX- EXECIO File I/O
A REXX exec becomes truly useful when it can read and write datasets. EXECIO is the TSO/E command that moves lines between a dataset and a REXX stem variable, and it is the standard way REXX programs do file I/O. This page covers allocating datasets, reading with DISKR, writing with DISKW, and finishes with a complete file-processing exec.
What EXECIO does
- EXECIO is a TSO/E host command, so inside REXX you run it as a quoted string: "EXECIO * DISKR INDD (STEM emp. FINIS)".
- DISKR reads lines from a dataset; DISKW writes lines to a dataset.
- The ddname (INDD above) must be allocated - with the ALLOCATE command in TSO, or with a JCL DD in batch.
- Data lands in a stem: stem.1, stem.2, ... and stem.0 holds the number of lines.
- After every EXECIO, the special variable RC holds the return code: 0 means success, anything else means a problem. Always check it.
Allocating a dataset to a ddname
- In a TSO session, allocate before the EXECIO and free afterwards:
/* REXX */
"ALLOC F(INDD) DA('MYID.EMP.DATA') SHR REUSE"
IF RC \= 0 THEN DO
SAY 'ALLOC failed, RC =' RC
EXIT 8
END
/* ... EXECIO work happens here ... */
"FREE F(INDD)"
- DA('MYID.EMP.DATA') - quote the dataset name or TSO prefixes it with your userid.
- SHR REUSE - share the dataset, and reuse the ddname if it is already allocated.
- FREE releases the dataset so it can be edited or deleted afterwards.
- In batch JCL you skip ALLOCATE entirely - the DD statements allocate the ddnames (see the JCL below).
Reading a dataset: DISKR
- "EXECIO * DISKR INDD (STEM emp. FINIS)" reads the whole dataset: * means all lines.
- STEM emp. collects the lines; emp.0 tells you how many were read.
- FINIS closes the dataset when the read completes.
- Replace * with a number to read a fixed count, and use (OPEN) / (FINIS) to process a huge dataset in chunks instead of loading it all at once.
/* REXX - read the whole file at once */
"EXECIO * DISKR INDD (STEM emp. FINIS)"
IF RC \= 0 THEN DO
SAY 'Read failed, RC =' RC
EXIT 8
END
SAY 'Records read:' emp.0
DO i = 1 TO emp.0
SAY i || ':' emp.i
END
/* REXX - chunked read for very large files */
"EXECIO 0 DISKR INDD (OPEN)"
DO FOREVER
"EXECIO 1000 DISKR INDD (STEM chunk.)"
IF chunk.0 = 0 THEN LEAVE
DO i = 1 TO chunk.0
/* process chunk.i here */
END
END
"EXECIO 0 DISKR INDD (FINIS)"
Writing a dataset: DISKW
- Build the output lines in a stem, set stem.0 to the line count, then write them:
/* REXX */
out.1 = 'First report line'
out.2 = 'Second report line'
out.0 = 2
"ALLOC F(OUTDD) DA('MYID.EMP.REPORT') SHR REUSE"
"EXECIO" out.0 "DISKW OUTDD (STEM out. FINIS)"
IF RC \= 0 THEN SAY 'Write failed, RC =' RC
"FREE F(OUTDD)"
- "EXECIO" out.0 "DISKW ..." - the line count comes from the REXX variable out.0.
- FINIS on a DISKW flushes the last lines and closes the dataset - without it, trailing records can be lost.
- To create a brand-new dataset from REXX, allocate it with space parameters, or simpler, define the output DD in JCL with DISP=(NEW,CATLG).
Complete example: employee salary report
Input dataset MYID.EMP.DATA holds fixed-format lines - id in columns 1-5, name in 6-25, salary in 26-33. The exec reads every record, totals the salaries, and writes a report. Batch JCL that runs it follows.
/* REXX - PAYRPT: read employees, write a totals report */
"ALLOC F(INDD) DA('MYID.EMP.DATA') SHR REUSE"
"EXECIO * DISKR INDD (STEM emp. FINIS)"
IF RC \= 0 THEN DO
SAY 'Read failed, RC =' RC
"FREE F(INDD)"
EXIT 8
END
total = 0
rep.1 = 'EMPLOYEE SALARY REPORT'
rep.2 = '======================'
n = 2
DO i = 1 TO emp.0
PARSE VAR emp.i 1 id 6 name 26 salary
total = total + salary
n = n + 1
rep.n = id STRIP(name) salary
END
n = n + 1
rep.n = '----------------------'
n = n + 1
rep.n = 'Employees:' emp.0 ' Total salary:' total
rep.0 = n
"ALLOC F(OUTDD) DA('MYID.EMP.REPORT') SHR REUSE"
"EXECIO" rep.0 "DISKW OUTDD (STEM rep. FINIS)"
IF RC \= 0 THEN SAY 'Write failed, RC =' RC
"FREE F(INDD) F(OUTDD)"
SAY 'Report written:' rep.0 'lines.'
//PAYRPT JOB (ACCT),'REXX PAYRPT',CLASS=A,MSGCLASS=X
//STEP1 EXEC PGM=IRXJCL,PARM='PAYRPT'
//SYSEXEC DD DSN=MYID.REXX.EXEC,DISP=SHR
//INDD DD DSN=MYID.EMP.DATA,DISP=SHR
//OUTDD DD DSN=MYID.EMP.REPORT,DISP=OLD
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD DUMMY
- In batch, INDD and OUTDD come from JCL DDs, so the exec needs no ALLOCATE - the same exec runs in TSO with ALLOCATE or in batch with DDs.
- PARSE VAR emp.i 1 id 6 name 26 salary splits each fixed-format record by column position.
- EXIT 8 on read failure sets a non-zero step return code so the job visibly fails.
