Datasets and Mainframe Utilities
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), which says nothing about data set allocation or about utility programs, because those belong to the operating system and not to the language; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2, where every item here lives; and against GnuCOBOL 3.2 (28 July 2023, the latest release), which was used for the one portable example, a file check on the PC that was run on 2026-10-20. Everything about JCL, IDCAMS, DFSORT, SDSF, REXX and the Zowe CLI is "z/OS; not run here": it was checked statement by statement against the z/OS 3.2 and Zowe documentation, but it was never executed because no z/OS system was available.
A COBOL program never creates its own data sets on z/OS. The SELECT clause names a ddname, the job binds the ddname to a data set, and the data set already exists, with a record format, a record length and some space chosen by someone. This page is about that someone: how a data set comes into being (three ways), what RECFM, LRECL and BLKSIZE must say so that the program’s FD and the data set agree, and the small IBM programs that copy, compare, sort, list and delete data sets between COBOL steps. The concepts of data sets, catalogs, ISPF and SDSF are introduced in Mainframe Environment Primer; the JOB, EXEC and DD statements themselves, return codes and the nightly job are in JCL for COBOL Programmers and are used here without being explained again. The FD side of the agreement is in File Concepts, SELECT and FD and Sequential Files; VSAM clusters and the DEFINE CLUSTER job are in Indexed Files and VSAM; the COBOL SORT statement and the basic DFSORT job are in Sort and Merge. The same tasks from a PC are on Modern Tooling with VS Code and Zowe.
Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapters 3 and 7) was written for z/OS 2.4 and Zowe version 1. The data set concepts and the utility names it uses (IEFBR14, IEBGENER, IEBCOPY, IDCAMS, DFSORT) are still current in z/OS 3.2 with the same control statements, but its Zowe command line is not: the CLI is now the 3.x line, and every zowe zos-files command on this page was checked against the current Zowe documentation. The book’s "copy library" job is an IEBCOPY step, which copies members between partitioned data sets; it is not what makes a COPY statement in a program work. The compiler finds copybooks through the SYSLIB DD of the compile step, as Copybooks and REPLACE explains, and IEBCOPY only fills the library that SYSLIB points to. The book mentions CLIST and REXX in a survey of the languages found on the mainframe; this page treats REXX in one short section, as a tool for small automation, and CLIST is not covered because REXX has replaced it for new work.
Three ways to allocate a data set
Whatever the way, the result is the same: a catalog entry for a name such as USERID.CIRC.LOANTRAN, space on a volume, and a set of attributes (organization, record format, record length, block size). The diagram shows the three routes of this section reaching it.
ISPF option 3.2
Option 3.2 of ISPF is the Data Set Utility, and typing 3.2 on the primary option menu goes straight to it. On its panel, option A allocates a new data set, R renames, D deletes, and leaving the option blank displays the information of the data set named in the fields below. Allocating shows a panel with the fields of the next table (the panel layout, defaults and some field names depend on the ISPF level and on the installation, and the ISPF help, PF1, explains each field).
| Field | Value for USERID.CIRC.LOANTRAN |
|---|---|
Data set name |
|
Volume serial, generic unit |
Left blank when the installation’s storage management picks the volume |
Space units, primary quantity, secondary quantity |
|
Directory blocks |
0 for a sequential data set; a number for a partitioned data set, which has a directory of members |
Record format |
|
Record length |
30 |
Block size |
0, so that the system chooses (see RECFM, LRECL and BLKSIZE) |
Data set name type |
Blank or |
ISPF is the fastest way for a single data set that someone will edit by hand, such as a copybook library USERID.CIRC.COPYLIB (FB, 80, directory blocks 20) or the source library USERID.CIRC.COBOL. It is the wrong way for anything that has to be repeated: the allocation is not recorded anywhere, so the next environment gets a different one.
JCL: the DD statement and IEFBR14
A new data set is allocated by a DD statement that says DISP=(NEW,CATLG,DELETE) and gives its space and attributes. The statement is carried out by the initiator when the step starts, before the program runs, so a job step whose only purpose is to allocate can run a program that does nothing. That program is IEFBR14: it returns completion code 0 and nothing else, and the allocation and the disposition processing at the end of the step do the work. The following job (z/OS; not run here) creates the transaction data set of the Circulation scenario and a copybook library, and deletes a work file.
//ALLOC JOB (ACCT),'ALLOCATE CIRC',CLASS=A,MSGCLASS=X
//STEP1 EXEC PGM=IEFBR14
//LOANTRAN DD DSN=USERID.CIRC.LOANTRAN,DISP=(NEW,CATLG,DELETE),
// RECFM=FB,LRECL=30,BLKSIZE=0,
// SPACE=(TRK,(5,1),RLSE)
//COPYLIB DD DSN=USERID.CIRC.COPYLIB,DISP=(NEW,CATLG,DELETE),
// DSNTYPE=LIBRARY,RECFM=FB,LRECL=80,BLKSIZE=0,
// SPACE=(TRK,(15,5,20))
//OLDWORK DD DSN=USERID.CIRC.WORK,DISP=(MOD,DELETE,DELETE),
// SPACE=(TRK,(1,1))
-
The ddnames (
LOANTRAN,COPYLIB,OLDWORK) mean nothing toIEFBR14; they only have to be unique in the step. -
DISP=(NEW,CATLG,DELETE)creates the data set, catalogs it when the step ends normally and deletes it if the step abends. Without the catalog request, a new data set is lost at the end of the job unless it is passed to a later step. -
SPACE=(TRK,(5,1),RLSE)asks for 5 tracks, up to 1 more track at a time when full, and the release of unused space when the data set is closed after being written by a program.RLSEdoes nothing in a step that does not open the data set, as here, but it is the habit to carry to theDDstatements of the writing steps. For a partitioned data set the third value of the inner list is the number of directory blocks, 20 here. -
DSNTYPE=LIBRARYmakes a PDSE, a partitioned data set extended; leaving it out gives a PDS, which has to be compressed from time to time (see IEBCOPY). -
BLKSIZE=0makes the system determine the block size, which is the normal practice (see RECFM, LRECL and BLKSIZE). -
DISP=(MOD,DELETE,DELETE)on a data set that may not exist is the usual idiom for "delete it if it is there":MODon a data set that does not exist is treated likeNEW, so the step creates it and the disposition deletes it again, and the job does not fail with "data set not found". Running the job twice is therefore safe, except for the first twoDDstatements, which fail the second time because the names already exist. A job that is run again and again uses this idiom first and allocates afterwards.
The same DD statements in the program’s own step allocate the data set at the moment it is needed, so a separate IEFBR14 step is only worth having to prepare the data sets before a run, or to clean up after it.
Zowe CLI: zowe zos-files create
The Zowe CLI creates data sets from a workstation through the z/OS Management Facility (z/OSMF) REST services, with no JCL. The command group is zowe zos-files create, and its sub-commands of the current documentation are data-set-sequential (alias ps), data-set-partitioned (alias pds), data-set-classic (alias classic, for JCL, assembler or COBOL source libraries), data-set-binary, data-set-c, data-set-vsam, uss-directory, uss-file, zos-file-system and the generic data-set, which creates a data set from an existing one’s properties. The connection comes from a z/OSMF profile or the team configuration (host, port, user and certificate are options of every command, and are normally kept in the profile, not typed). The commands below (z/OS; not run here) allocate the same objects as the job above:
zowe zos-files create data-set-sequential USERID.CIRC.LOANTRAN --record-format FB --record-length 30 --block-size 27990 --size 5TRK
zowe zos-files create data-set-partitioned USERID.CIRC.COPYLIB --data-set-type LIBRARY
zowe zos-files create data-set USERID.CIRC.LOANSRT --like USERID.CIRC.LOANTRAN
-
The first command sets the format, the length, the block size and the space explicitly.
--sizetakes a number followed byCYLorTRKand sets the primary allocation; the secondary allocation then becomes about 10 percent of it. The defaults ofdata-set-sequentialare, per the documentation,FB, a record length of 80 and a block size of 6160, so a data set for 30-byte records must say so. The block size 27990 is 933 records of 30 bytes, the largest multiple of 30 that fits in the 27998-byte half-track block that is usual on a 3390 disk; the next section shows why a multiple of the record length is needed, andBLKSIZE=0in JCL achieves the same without the arithmetic. -
The second command takes the defaults of
data-set-partitioned(FB, 80, 5 directory blocks) and--data-set-type LIBRARYmakes it a PDSE, asDSNTYPE=LIBRARYdoes in JCL. The other values of--data-set-typelisted by the documentation areBASIC,EXTPREF,EXTREQ,HFS,LARGE,PDSandPIPE. -
The third command creates a data set with the attributes of an existing one, which is the quick way to make a work file the same shape as a master.
--likecan be combined with an override such as--record-length. -
The space is given with
--sizehere, whose unit is explicit; if--primary-spaceis used instead, check in--helpwhich unit applies before relying on it, because the documentation page does not state it.
The rest of the daily commands of the group, again checked against the documentation, are:
zowe zos-files list data-set "USERID.CIRC.*" --attributes
zowe zos-files upload file-to-data-set LOANTRAN USERID.CIRC.LOANTRAN
zowe zos-files download data-set USERID.CIRC.LOANTRAN --file LOANTRAN --extension ""
zowe zos-files delete data-set USERID.CIRC.WORK --for-sure
list data-set accepts a name or a pattern, and --attributes adds the details of each match; upload file-to-data-set copies a local file to a data set or to a member written as name(member), with --binary for no conversion and --encoding for a conversion you choose; download data-set writes a local file whose name and extension are the options --file and --extension (the default extension is .txt); and delete data-set needs --for-sure, which is the CLI’s confirmation that the deletion is permanent. When a text file is uploaded into a fixed-length data set, check with the list command and a browse in ISPF that the records came out with the length the FD expects; that detail depends on the conversion and was not tested here.
RECFM, LRECL and BLKSIZE
These three attributes, usually called the DCB attributes after the data control block that holds them, tell the access method how the bytes of the data set are cut into records and blocks. They are the connection between the data set and the program, and a mismatch is the commonest allocation problem of a COBOL developer.
| Attribute | What it says |
|---|---|
|
|
|
The logical record length in bytes. For |
|
The physical block length in bytes. For |
How the FD produces the attributes of the data set it opens is the subject of the files pages; the rule of thumb for the Circulation records is below. The attributes can be given in three places (the data set label when it was created, the DD statement of the step, and the program), and the sources are merged when the file is opened, with the values of the program taking part through its FD and the DD statement overriding what the label says.
| Data set | Record in the FD |
RECFM and LRECL |
BLKSIZE |
|---|---|---|---|
|
|
|
0 (system-determined) |
|
|
|
0 (system-determined) |
A report file |
A 133-byte print line with a carriage-control byte |
|
0 (system-determined) |
A variable-length extract with records of 10 to 100 bytes |
|
|
0 (system-determined) |
Enterprise COBOL takes the record format from the FD when it contains RECORDING MODE, and otherwise infers it from the record descriptions; BLOCK CONTAINS 0 RECORDS in the FD hands the choice of the block size to the system, the standing practice on z/OS (see Sequential Files). The practical consequences are these:
-
The record length of the
FDand theLRECLof the data set are the same number for fixed records. A 60-byteBOOK-RECORDread from a data set withLRECL=80is a mismatch, and the program typically fails to open it with a file status that reports conflicting attributes (status39, see File Status Codes) instead of reading shifted records. -
A block size of an
FBdata set must be a multiple of the record length. When the system is asked to determine it (BLKSIZE=0) it picks a suitable value for the device the data set lands on, so coding a number by hand is only useful to reproduce an old size; the 27990 of the Zowe example above is such a value for 30-byte records on a 3390 disk. -
A copy whose output record length differs from the input is no longer a plain copy:
IEBGENERneeds control statements that describe the editing (see IEBGENER and ICEGENER), and DFSORT reformats with its own statements. Check the manual before copying between different lengths. -
Running out of space ends a step with a system abend of the
x37family (B37,D37andE37, whose common causes are a full volume, a primary quantity filled with no secondary quantity given, and no further extent available). The cure is a largerSPACE(and a secondary quantity), not a change to the program.
The IBM utilities a COBOL developer meets
IBM supplies a family of small programs that a job step runs with EXEC PGM= and that a COBOL developer uses to prepare, move and inspect data between steps. Each takes its work from DD statements and, where it needs more detail, from control statements in SYSIN, and writes its report to SYSPRINT (or SYSOUT for DFSORT). Their return code (0 good, 4 warning, 8 or more error, by the usual convention) is tested in the JCL like that of any step.
| Utility | What it does | Use in the Circulation job |
|---|---|---|
|
Does nothing; the |
Prepare or remove the working data sets before and after the nightly run |
|
Copies a sequential data set, or builds partitioned members from sequential input; can print and edit |
Back up |
|
Copies, merges, compresses and unloads partitioned data sets (PDS and PDSE), selecting members |
Move compiled programs and copybooks between libraries; compress a PDS |
|
Access method services: defines, copies, lists and deletes VSAM clusters and catalog entries |
Define and load |
|
Sorts, merges, copies, filters, reformats, summarizes and reports on records |
Order |
SDSF |
The ISPF application that displays jobs and their spool output |
Read the |
On a PC the same chores are touch, cp, cat, ls, sort and rm, and the GnuCOBOL example at the end of the page does a few of them on the Circulation file. The program and the utilities are chained in the nightly run described on JCL for COBOL Programmers.
IEBGENER and ICEGENER
IEBGENER copies the sequential data set named by the SYSUT1 DD to the one named by SYSUT2. SYSPRINT receives the utility’s messages, and SYSIN holds control statements, or is DUMMY when no editing is wanted. The IBM example that prints a data set uses exactly that shape, with SYSUT2 going to the printer class; the following job (z/OS; not run here) makes a backup copy of the loan transactions and then lists the copy on the job output:
//BACKUP EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSUT1 DD DISP=SHR,DSN=USERID.CIRC.LOANTRAN
//SYSUT2 DD DSN=USERID.CIRC.LOANTRAN.BACKUP,DISP=(NEW,CATLG,DELETE),
// RECFM=FB,LRECL=30,BLKSIZE=0,SPACE=(TRK,(5,1),RLSE)
//LISTING EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSUT1 DD DISP=SHR,DSN=USERID.CIRC.LOANTRAN.BACKUP
//SYSUT2 DD SYSOUT=*
-
With
SYSIN DD DUMMYthere is no editing, so the record length and the format of the output come from the input data set and the system selects the block size, as in the IBM example. -
The same program also copies an in-stream or sequential data set into a member of a PDS (
SYSUT2namesUSERID.CIRC.COPYLIB(LOANREC), a member, not a whole library), which is how a copybook is loaded from a job. The control statementsGENERATEandRECORDdescribe editing and the creation of several members; they are rarely needed for the Circulation job and are described in the IBM manual. -
ICEGENERis the DFSORT front end that does the same unedited copy, generally faster. The IBM manual advises it in preference toIEBGENERfor unedited copies when DFSORT is installed, and notes that it may be installed under the nameIEBGENER, so that the job above may in fact run it. Both take the same DD names. -
IEBGENERcopies sequential data sets and members; it cannot copy a VSAM cluster (IDCAMS REPROdoes that) and cannot copy a whole PDS in one step (IEBCOPYdoes that).
IEBCOPY
IEBCOPY works on partitioned data sets, the libraries that hold source members, copybooks, JCL procedures and load modules. It copies or merges members between PDSs or PDSEs, unloads a library to a sequential data set and reloads it, selects or excludes members by name, renames and replaces them, and compresses a PDS. Its DD statements are SYSPRINT (messages), the input and output libraries under ddnames of your choice, work data sets SYSUT3 and SYSUT4 (the spill data sets of the IBM examples; the unit name SYSDA is an installation choice), and SYSIN with the control statements. The following job (z/OS; not run here) copies the copybooks BOOKREC, MEMBREC and LOANREC from the development library to the test library, and then compresses the development library in place:
//COPYMEM EXEC PGM=IEBCOPY
//SYSPRINT DD SYSOUT=*
//SYSUT3 DD UNIT=SYSDA,SPACE=(TRK,(1,1))
//SYSUT4 DD UNIT=SYSDA,SPACE=(TRK,(1,1))
//INLIB DD DISP=SHR,DSN=USERID.CIRC.COPYLIB
//OUTLIB DD DISP=OLD,DSN=USERID.CIRC.TEST.COPYLIB
//SYSIN DD *
COPY OUTDD=OUTLIB,INDD=INLIB
SELECT MEMBER=(BOOKREC,MEMBREC,LOANREC)
/*
//COMPRESS EXEC PGM=IEBCOPY
//SYSPRINT DD SYSOUT=*
//A DD DISP=OLD,DSN=USERID.CIRC.COPYLIB
//B DD DISP=OLD,DSN=USERID.CIRC.COPYLIB
//SYSIN DD *
COPY OUTDD=B,INDD=A
/*
-
COPY OUTDD=...,INDD=...names the ddnames of the output and the input; aSELECT MEMBER=(...)statement after it limits the copy to the listed members (andEXCLUDEdoes the reverse). A selected member can be written with the replace option, as inSELECT MEMBER=((LOANREC,,R))(the form used in the IBM example), to replace a member of the same name in the output; without it, check inSYSPRINTwhether the member was copied, because a changed copybook that was not replaced is a classic source of builds that use old code. -
The second step is the compress-in-place form: the same data set under two ddnames, one as input and one as output, and no
SELECT. The IBM manual warns to take a backup first, because an interruption during a compress can destroy the library, and not to compress a library other users are using at the time. Compression does not apply to a PDSE, which does not need it, and the need to do this on a PDS is one of the reasons to prefer a PDSE for new libraries. -
The utility reports the members copied, replaced or not copied in
SYSPRINT, which is where to look first when a build does not see a changed copybook.
IDCAMS
IDCAMS is the access method services program. Its commands in SYSIN define VSAM clusters, copy data in and out of them and manage catalog entries. DEFINE CLUSTER, REPRO and PRINT for the Circulation book master, with every parameter explained, are in Indexed Files and VSAM; this section adds the commands for looking at the catalog and for cleaning up, and the way an IDCAMS step reports failures. The following job (z/OS; not run here) lists the Circulation data sets, deletes a cluster so that a job can define it again, and saves the cluster’s records to a sequential data set:
//CATALOG EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
LISTCAT LEVEL(USERID.CIRC)
LISTCAT ENTRIES(USERID.CIRC.BOOKMAST) ALL
DELETE USERID.CIRC.BOOKOLD CLUSTER PURGE
IF MAXCC = 8 THEN SET MAXCC = 0
REPRO INDATASET(USERID.CIRC.BOOKMAST) -
OUTDATASET(USERID.CIRC.BOOKBAK)
/*
-
LISTCAT LEVEL(USERID.CIRC)lists the catalog entries whose names start with those qualifiers;ENTRIES(name)withALLlists one entry with all its fields, including the attributes (key length and offset, record size, control interval size, space and the high-used values). A listing like this is the quickest way to check that an allocation did what was intended. -
DELETE ... CLUSTER PURGEremoves a VSAM cluster, its components and its catalog entry;PURGEdeletes it even if its retention period has not expired. For a non-VSAM data set the entry type isNONVSAM, andSCRATCH(the removal from the volume as well as from the catalog) orNOSCRATCHdecides whether the data is removed from the disk. -
DELETEof an entry that does not exist gives a condition code of 8 with a message that the entry was not found (a message of the familyIDC3012I).IF MAXCC = 8 THEN SET MAXCC = 0resets the step’s code, so that a clean-up that finds nothing to delete does not stop the job; the hyphen at the end of a line continues anIDCAMScommand on the next line. -
REPRO INDATASET(...) OUTDATASET(...)copies the records; with a VSAM input and a sequential output data set that has been allocated withRECFM=FBandLRECL=60first (for example with theIEFBR14job above), it makes a flat backup thatIEBGENERor a COBOL program can read.REPROacceptsFROMKEY,TOKEY,SKIPandCOUNTto select a range. -
The return code of the
IDCAMSstep is the highest condition code of its commands (MAXCC), and the messages (IDC...I) inSYSPRINTexplain every non-zero code.
DFSORT and ICETOOL
The basic DFSORT job, with SORT FIELDS, INCLUDE, EQUALS and a first ICETOOL step, is on Sort and Merge; this section adds the operations that are used between steps to split and to check files. The following ICETOOL job (z/OS; not run here) splits the sorted transactions into one data set per transaction type with OUTFIL, then reports the number of records and statistics of the book number, and lists the records in a printed report:
//TOOLRUN EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//IN DD DISP=SHR,DSN=USERID.CIRC.LOANSRT
//OUTCHK DD DSN=USERID.CIRC.LOANCHK,DISP=(NEW,CATLG,DELETE),
// RECFM=FB,LRECL=30,SPACE=(TRK,(5,1),RLSE)
//OUTRET DD DSN=USERID.CIRC.LOANRET,DISP=(NEW,CATLG,DELETE),
// RECFM=FB,LRECL=30,SPACE=(TRK,(5,1),RLSE)
//RPT DD SYSOUT=*
//CTL1CNTL DD *
OUTFIL FNAMES=OUTCHK,INCLUDE=(1,1,CH,EQ,C'O')
OUTFIL FNAMES=OUTRET,INCLUDE=(1,1,CH,EQ,C'R')
/*
//TOOLIN DD *
COPY FROM(IN) USING(CTL1)
COUNT FROM(IN)
STATS FROM(IN) ON(8,6,ZD)
DISPLAY FROM(IN) LIST(RPT) TITLE('Loan transactions') ON(1,1,CH) ON(2,6,ZD) ON(8,6,ZD) ON(14,8,ZD)
/*
-
The statements of
CTL1CNTLare ordinary DFSORT control statements (USING(CTL1)on an operator means "readCTL1CNTL`"). `OUTFIL FNAMES=ddname,INCLUDE=(...)writes to the named DD only the records that satisfy the condition, so one pass over the input produces both output data sets. The positions are those ofLOANREC: type at 1, member at 2, book at 8, date at 14. -
COPYandSORTmove records from theFROMdata set to theTOdata set, or to theOUTFILoutputs of the control data set.COUNTwrites a message with the number of records,STATSreports statistics (minimum, maximum, total and average) of a numeric field given inON(position,length,format), here the six zoned-decimal digits of the book number, andDISPLAYproduces a report of the listed fields on theLISTDD. These operators are the building blocks of a reconciliation step: the count of the input must equal the sum of the counts of the outputs, and a job step that tests it turns a silent loss of records into a non-zero return code. -
TOOLMSGreceives ICETOOL’s messages andDFSMSGthose of DFSORT, one of each is needed in every ICETOOL step.
For a one-off check, nothing replaces the messages in DFSMSG and TOOLMSG: they show the number of records read, written, deleted and inserted by each operator, which is the first thing to compare with the expected counts.
SDSF
SDSF, reached from the ISPF menu where it is installed, shows the jobs on the JES2 queues and the output of each one. The panels, PREFIX, OWNER, and the row commands S, ? and P are in Mainframe Environment Primer. For utilities the habit is the following: open the job, find the step’s SYSPRINT (or TOOLMSG, DFSMSG, SYSTSPRT) data set among its spool files, read the messages from the first one that is not informational, and compare the step’s return code with the expectation. The IBM utilities number their messages with a prefix of their own (IEB for IEBGENER and IEBCOPY, IDC for IDCAMS, ICE for DFSORT and ICETOOL), and the message id is what to look up in the manuals.
The same information is available without ISPF. The Zowe CLI submits a job from a data set, lists jobs by name prefix and owner, and views the spool files; the following commands are checked against the Zowe documentation (z/OS; not run here):
zowe zos-jobs submit data-set "USERID.CIRC.JCL(ALLOC)" --view-all-spool-content
zowe zos-jobs list jobs --prefix "ALLOC*"
zowe zos-jobs view all-spool-content JOB00234
The first submits the job and prints the whole job output once it ends, and the third prints every spool file of a job whose id (here an example value, JOB00234, the one used in the Zowe documentation) the second command listed. SDSF also has a REXX interface and a Java interface for programs that process the spool, described in the SDSF Operation and Customization guide; for a COBOL developer that is only a pointer, to use when the output of many jobs has to be searched by a script.
REXX for small automation
REXX is the scripting language of z/OS (it also runs on z/OS UNIX and on PCs, and the language is not the subject here). On z/OS a REXX program, called an exec, is a member of a partitioned data set, and it drives TSO commands by sending them as strings: any statement that is a string expression is passed to the current host environment, which in TSO is ADDRESS TSO. For a COBOL developer REXX is the quick way to check an allocation, count records, build a list of members, or script an ISPF task, without compiling a program. The following exec (z/OS; not run here) checks the Circulation transaction data set against what the FD expects, reads it and counts the transactions by type:
/* REXX - LOANCHK: check and count USERID.CIRC.LOANTRAN */
dsn = "'USERID.CIRC.LOANTRAN'"
x = LISTDSI(dsn)
if x > 4 then do
say "LOANCHK: cannot read" dsn", reason" SYSREASON
exit 8
end
say "RECFM="SYSRECFM "LRECL="SYSLRECL "BLKSIZE="SYSBLKSIZE
if SYSLRECL <> 30 then do
say "LOANCHK: LRECL is not 30, the FD would not match"
exit 8
end
address TSO
"ALLOC FI(LOANIN) DA("dsn") SHR REUSE"
if rc > 0 then do
say "LOANCHK: ALLOC failed with return code" rc
exit 8
end
"EXECIO * DISKR LOANIN (STEM rec. FINIS"
if rc > 0 then do
say "LOANCHK: EXECIO failed with return code" rc
"FREE FI(LOANIN)"
exit 8
end
"FREE FI(LOANIN)"
out = 0; ret = 0; bad = 0
do i = 1 to rec.0
select
when substr(rec.i,1,1) = "O" then out = out + 1
when substr(rec.i,1,1) = "R" then ret = ret + 1
otherwise bad = bad + 1
end
end
say rec.0 "records:" out "checkouts," ret "returns," bad "rejects"
if bad > 0 then exit 4
exit 0
-
LISTDSIis called as a function, and returns 0 when it found the data set and set its variables, 4 when some information is not available, and 16 when it failed (then onlySYSREASONis valid, with codes such as 24 for "data set not found").SYSRECFM,SYSLRECLandSYSBLKSIZEhold the three attributes of this page, andSYSDSORGthe organization (PS,PO,VS). For a VSAM data set most of the variables are not accurate, so the check suits sequential and partitioned data sets. The fully qualified name is inside single quotes; without them the user’s prefix would be put in front of it. -
ALLOCandFREEare TSO commands that give the ddnameLOANINto the data set and release it again;EXECIOdoes not allocate for you.EXECIO * DISKR LOANIN (STEM rec. FINISreads every record intorec.1,rec.2and so on, puts the number of records inrec.0and closes the file. Its return code is 0 for normal completion and 20 for a severe error. The documentation advises putting allEXECIOoperands in quotation marks so that they cannot be replaced by variables of the same name, which the whole string does here. -
exit 4andexit 8give the exec a return code that a job can test. To run the exec in batch, the TSO terminal monitor programIKJEFT01runs it, with the library of execs onSYSEXECand the commands onSYSTSIN(z/OS; not run here):
//LOANCHK EXEC PGM=IKJEFT01
//SYSEXEC DD DISP=SHR,DSN=USERID.CIRC.REXX
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
%LOANCHK
/*
What SAY prints goes to SYSTSPRT. For anything larger than a script (reading a million records, say, or updating a data set), a COBOL program or a DFSORT step is the right tool: an exec that reads all records into a stem keeps them all in memory.
The same chores on a PC with GnuCOBOL
GnuCOBOL has no catalog and no DD statements: a "data set" is an ordinary file, its name comes from the assignment name or from a DD_name environment variable (see File Concepts, SELECT and FD), and there is no RECFM, LRECL or BLKSIZE to set. A LINE SEQUENTIAL file is the Circulation record followed by a newline, so a 30-byte record occupies 31 bytes. The utilities of this page therefore become ordinary shell commands. The session below was run on 2026-10-20 in the GnuCOBOL 3.2 container, in a directory holding the four-record LOANTRAN file of the scenario. wc counts, awk shows the length of each record, cp plays IEBGENER, cmp compares, an empty file is what IEFBR14 allocates, and rm deletes:
mkdir -p circ && cd circ
cp ../LOANTRAN LOANTRAN
echo "records : $(wc -l < LOANTRAN)"
echo "bytes : $(wc -c < LOANTRAN)"
awk '{ print length($0) }' LOANTRAN | sort -u | sed 's/^/length : /'
cp LOANTRAN LOANCOPY
cmp LOANTRAN LOANCOPY && echo "copy identical"
: > LOANEMPTY
ls -l LOANEMPTY | awk '{print "empty : " $5 " bytes"}'
rm LOANCOPY LOANEMPTY
ls
The run printed:
records : 4
bytes : 124
length : 30
copy identical
empty : 0 bytes
LOANTRAN
The 124 bytes are the four records of 30 bytes plus four newlines, and the single length of 30 shows that every record has the length that LOAN-RECORD expects, which is the check that LISTDSI and LRECL perform on z/OS. The LOANCOPY and LOANEMPTY files were created and removed again, which is the clean-up that DISP=(MOD,DELETE,DELETE) performs in the IEFBR14 job. The sort, filter and count of the DFSORT section are sort -s, grep and wc -l on the PC, and the variable-length records of a RECORD VARYING file are stored by GnuCOBOL with its own 4-byte length prefix, not the RDW (see Sequential Files).
Common mistakes
-
Forgetting
LRECLwhen allocating. The defaults of the tools suit 80-byte records (the Zowedata-set-sequentialdefaults areFB, 80 and 6160), so a data set meant for 30-byte records must sayLRECL=30or--record-length 30, or the program fails to open it with a conflict of attributes. -
Coding a block size that is not a multiple of the record length. For
FB, useBLKSIZE=0and let the system choose. -
Allocating without
CATLG.DISP=(NEW,PASS)orDISP=NEWalone leaves a data set that is gone at the end of the job; a later step or job cannot find it. -
Running an allocation job twice. A
NEWon a name that is already cataloged fails the step. Delete first (DISP=(MOD,DELETE,DELETE)orIDCAMS DELETEfollowed bySET MAXCC = 0), or allocate with--likein a script that checks first. -
Using
IEBGENERto copy a VSAM cluster orIEBCOPYto copy a sequential file.IEBGENERandIEBCOPYdo not handle VSAM; useIDCAMS REPRO.IEBCOPYis for partitioned data sets only. -
Compressing a PDS that is in use, or without a backup. Other users may see the library as damaged while it is being compressed, and an interrupted compress can destroy it. Use a PDSE for new libraries.
-
Believing that
IEBCOPYof the copybook library makesCOPYwork. The compile step finds copybooks throughSYSLIB; the copy only moves members into the librarySYSLIBnames. -
Ignoring the return code of a utility step. A step that ends with return code 8 and a job that carries on with an empty file gives a nightly run with no transactions; test the code with
CONDorIFin the JCL. -
Quoting a data set name wrongly in REXX or Zowe. In a REXX exec a name without single quotes gets the user’s prefix added; in a shell the quotes of a pattern such as
"USERID.CIRC.*"stop the shell from expanding it. -
Using a REXX exec for bulk data.
EXECIOinto a stem holds every record in memory; use DFSORT or a COBOL program for large files.