cheat sheet

IEBCOPY

IBM utility for copying, merging, compressing, unloading, and reloading PDS and PDSE libraries on z/OS, including member selection, rename, and exclusion.

IEBCOPY — Partitioned dataset copy, compress, and unload

What it is

IEBCOPY is the IBM-supplied utility for everything you do to a PDS (partitioned dataset) or PDSE (Partitioned Data Set Extended) library beyond the level of a single member: copying members between libraries, merging two libraries, compressing free space out of a PDS, and unloading a PDS to a sequential dataset for transport or backup. It is shipped with every z/OS installation in SYS1.LINKLIB and is invoked exclusively from JCL via PGM=IEBCOPY — there is no TSO command equivalent. Reach for IEBCOPY when you need to ship a load library between systems via FTP, refresh a copybook library after a code merge, recover wasted directory space in a heavily edited PDS, or batch-promote selected members from a development library to production; for VSAM clusters use IDCAMS instead, and for a single member edit-and-save the ISPF editor is faster.

Invocation

The utility runs as a single-step batch program. Three DD names are required (SYSPRINT, SYSIN, and at least one input/output pair), and two work files (SYSUT3, SYSUT4) are required only when the input or output uses a non-default block size; modern z/OS allocates these automatically but explicit allocation is still common in production JCL.

text
//IEBCOPY  EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUT3   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT4   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//INDD     DD DSN=ALICE.SOURCE.PDS,DISP=SHR
//OUTDD    DD DSN=ALICE.TARGET.PDS,DISP=OLD
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*

The DD names INDD and OUTDD shown above are arbitrary tags — anything 1-8 characters works, and the COPY statement refers to them by ddname, not dataset name. The convention in older shops is SYSUT1 (input) and SYSUT2 (output), inherited from the days when IEBCOPY only supported one input and one output.

Essential DD statements

DDPurpose
SYSPRINTRequired. Receives the IEBCOPY report — counts of members copied, replaced, skipped.
SYSINRequired. Holds the control statements (COPY, SELECT, EXCLUDE, COPYGRP, COMPRESS).
SYSUT3Optional work file used when input and output BLKSIZE differ.
SYSUT4Optional work file used during directory rebuild.
SYSUT1Legacy name commonly used as the input DD.
SYSUT2Legacy name commonly used as the output DD.
ddnameAny 1-8 character DD pointing at a PDS, PDSE, or sequential unload dataset.

Control statements

Every line in SYSIN is either a COPY, SELECT, EXCLUDE, COPYGRP, or COMPRESS statement. Continuation is signalled by a non-blank character in column 72 followed by the rest of the statement starting in column 16 on the next line. The full statement set is small:

StatementPurpose
COPYBegin a copy group; specifies INDD/OUTDD pair(s).
SELECTList members to copy from the most recent COPY.
EXCLUDEList members to skip from the most recent COPY.
COPYGRPPDSE-only — copy a member alias group as a unit.
COMPRESSPDS-only — reclaim free directory blocks and member space in place.

COPY — the workhorse

COPY names one or more input DDs and exactly one output DD. With no following SELECT or EXCLUDE, IEBCOPY copies every member of every input. Multiple input DDs are merged into the output in the order listed; on a name collision, the last copy wins unless replacement is suppressed.

text
//SYSIN    DD *
  COPY INDD=((LIB1,R),(LIB2,R)),OUTDD=TARGET
/*

The ,R modifier on each INDD entry means "replace if the member already exists in the output." Without R, a duplicate member name in the output is left untouched and IEBCOPY emits message IEB1014I MEMBER NOT REPLACED.

A minimal merge of three libraries into a fresh PDS:

text
//IN1      DD DSN=ALICE.LIB.A,DISP=SHR
//IN2      DD DSN=ALICE.LIB.B,DISP=SHR
//IN3      DD DSN=ALICE.LIB.C,DISP=SHR
//OUT      DD DSN=ALICE.LIB.MERGED,DISP=OLD
//SYSIN    DD *
  COPY OUTDD=OUT,INDD=((IN1,R),(IN2,R),(IN3,R))
/*

SELECT — member-level inclusion

SELECT filters which members of the preceding COPY are processed. The basic form is MEMBER=(M1,M2,M3); the powerful form adds a rename and a replace flag for each entry.

text
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
    SELECT MEMBER=(PROG01,PROG02,PROG03)
/*

The tuple form (oldname,newname,R) renames a member during copy and forces replacement at the destination:

text
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
    SELECT MEMBER=(                            -
                   (OLDNAME,NEWNAME,R),        -
                   (PROG01,PROG01V2,R),        -
                   COPYBOOK1,                  -
                   COPYBOOK2                   -
                  )
/*

Within one SELECT, mix bare names and tuples freely. The R in a tuple is per-member and overrides the absence of R on the INDD spec.

EXCLUDE — member-level exclusion

EXCLUDE is the inverse of SELECT: copy everything in the input library except the listed members. Useful when the exception list is shorter than the include list, or when you want to suppress test-only members from a production promotion.

text
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
    EXCLUDE MEMBER=(SCRATCH,TEMP01,TEMP02,$DEBUG)
/*

A single COPY may carry exactly one SELECT or one EXCLUDE, never both. To do both, write two COPY statements pointing at the same input.

COPYGRP — PDSE alias groups

PDSE libraries support member aliases — multiple names pointing at one stored copy. A program load module with multiple entry-point names is the canonical example. COPYGRP copies the primary member and all of its aliases as one atomic unit; using SELECT on a PDSE would copy only the named alias without its companions and leave the target broken.

text
//INDD     DD DSN=ALICE.LOAD.PDSE,DISP=SHR
//OUTDD    DD DSN=ALICE.LOAD.NEW,DISP=OLD
//SYSIN    DD *
  COPYGRP OUTDD=OUTDD,INDD=((INDD,R))
    SELECT MEMBER=(MAINPROG,UTILMOD)
/*

For pure PDS (not PDSE), COPYGRP falls back to COPY behaviour with a warning — IEBCOPY does not refuse the statement but issues message IEB1135I COPYGRP NOT SUPPORTED FOR PDS, COPY USED.

COMPRESS — reclaim PDS free space

A PDS that has been heavily edited accumulates unused space: when a member is replaced, the old copy is orphaned in the dataset and the directory entry is just repointed at the new location. COMPRESS rebuilds the dataset in place, packing live members at the front and reclaiming the dead space; the directory itself is also recompacted. PDSE manages this automatically and rejects COMPRESS with message IEB1099I COMPRESS NOT VALID FOR PDSE.

text
//IEBCOPY  EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//MYPDS    DD DSN=ALICE.SOURCE.PDS,DISP=OLD
//SYSIN    DD *
  COMPRESS OUTDD=MYPDS
/*

DISP=OLD is mandatory — COMPRESS is destructive in place and the system must exclude all other users of the PDS for the duration. A common failure mode is the dataset being enqueued by an ISPF Edit session; the job ABENDs with IEC150I 913-38 until the editor is closed.

Unload — PDS to sequential dataset

"Unload" is not a separate verb — it is a COPY from a PDS or PDSE to a sequential dataset. The output dataset receives a special internal format that IEBCOPY can later reload, but no other utility can read sensibly. This is the canonical way to ship a library between systems: unload, FTP in binary mode, reload.

text
//UNLOAD   EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=ALICE.SOURCE.PDS,DISP=SHR
//OUTDD    DD DSN=ALICE.SOURCE.UNLOAD,
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(50,10)),
//             DCB=(RECFM=U,BLKSIZE=32760),
//             UNIT=SYSDA
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*

Key points:

  • RECFM=U (undefined) and BLKSIZE=32760 are required for the unload format. Any other DCB causes the reload step to fail with IEB1071I INVALID UNLOAD DATASET FORMAT.
  • Allocate the output with SPACE=(CYL,(prim,sec)) generous enough to hold every member of the source plus its directory. A rule of thumb is 1.2× the source PDS size.
  • The unload dataset is self-describing — the source PDS's DCB and directory are preserved inside.

Reload — sequential back to PDS

The reverse operation: COPY from the unload sequential dataset back to a PDS or PDSE. The target may have different DCB attributes than the original; IEBCOPY reblocks members on the fly. The target must be pre-allocated; IEBCOPY does not create the PDS for you.

text
//RELOAD   EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//ALLOC    EXEC PGM=IEFBR14
//NEWPDS   DD DSN=ALICE.SOURCE.NEW,
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(20,5,30)),
//             DCB=(RECFM=FB,LRECL=80,BLKSIZE=27920),
//             UNIT=SYSDA
//*
//RELOAD   EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=ALICE.SOURCE.UNLOAD,DISP=SHR
//OUTDD    DD DSN=ALICE.SOURCE.NEW,DISP=OLD
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*

To force the reload target to be a PDSE rather than a PDS, set DSNTYPE=LIBRARY on the IEFBR14 allocation:

text
//NEWPDSE  DD DSN=ALICE.SOURCE.PDSE,
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(20,5,30)),
//             DSNTYPE=LIBRARY,
//             DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
//             UNIT=SYSDA

Multiple COPY groups in one step

A single IEBCOPY step may contain any number of COPY statements. Each group is independent — different INDD/OUTDD combinations, different SELECT/EXCLUDE rules, and a non-zero return code from one group does not halt the next.

text
//SYSIN    DD *
  COPY INDD=SRCLIB,OUTDD=PRODLIB
    SELECT MEMBER=(MAINPGM,UTIL01,UTIL02)
  COPY INDD=COPYLIB,OUTDD=PRODLIB
    EXCLUDE MEMBER=(OBSOLETE,LEGACY)
  COPY INDD=PROCLIB,OUTDD=PROCDST
/*

PDS vs PDSE differences

The two library types coexist on every modern z/OS system and IEBCOPY handles both, but they differ in ways that matter for IEBCOPY operations:

AspectPDSPDSE
Free spaceAccumulates dead members until COMPRESSReclaimed automatically
Directory sizeFixed at allocation (SPACE=(...,dir))Auto-extends
Concurrent updatesOne updater at a timeMultiple updaters, member-level locking
Member aliasesOne name per stored copyMany aliases per stored copy (use COPYGRP)
COMPRESSRequired periodicallyRejected with IEB1099I
DSNTYPEDSNTYPE=PDS or omitted with SPACE=…,dirDSNTYPE=LIBRARY
Cross-system copyUnload/reload preserves directory formatSame — but alias groups need COPYGRP

When copying between the two types, IEBCOPY adapts: PDS-to-PDSE preserves member contents but cannot fabricate aliases; PDSE-to-PDS with COPYGRP results in only the primary member's content reaching the target (PDS can't hold aliases). Use SELECT with explicit tuples if you need to rename alias members into separate PDS entries.

Common pitfalls

  1. Forgetting ,R and silently skipping replacements — a re-run of an unchanged JCL job appears to succeed but the target still contains the old members. The IEBCOPY report shows MEMBER NOT REPLACED lines; always check the SYSPRINT count.
  2. Using COMPRESS on a PDSE — fails with IEB1099I. PDSE manages its own free space; no action needed.
  3. Open ISPF Edit on a PDS while running COMPRESS — job ABENDs with 913-38 (RACF/enqueue). Close all editors and any active jobs holding the dataset before the step starts.
  4. Wrong DCB on an unload dataset — anything other than RECFM=U,BLKSIZE=32760 produces an unreadable unload. The reload step fails with IEB1071I.
  5. SELECT on a PDSE with aliases — copies the named member without its aliases. Use COPYGRP for any PDSE that holds load modules with multiple entry-point names.
  6. Directory full — appears as IEC031I D37 on the OUTDD. PDS directories are fixed at allocation; either reallocate the target with a larger directory size (SPACE=(CYL,(prim,sec,dir))) or migrate to PDSE.
  7. Member name collision across multiple INDDs — without ,R on the INDD spec, the first occurrence wins. Order your INDD list intentionally if libraries have overlapping names.
  8. PARM='WORK=2M' — increases the in-memory work buffer for faster compress on very large PDS, but rarely needed on modern z/OS; the default 1M is usually enough.
  9. B37/D37/E37 ABENDs on the output PDS — output ran out of primary+secondary extents (B37), directory blocks (D37), or volume space (E37). Reallocate larger or, for D37 specifically, migrate to PDSE.
  10. Forgetting DISP=OLD for COMPRESSDISP=SHR is silently accepted by JCL but the compress operation fails with an enqueue conflict at runtime.

Sources

References consulted while writing this article. Links open in a new tab.

  • IBM Documentation — IEBCOPY (z/OS DFSMSdfp Utilities) — Authoritative reference for IEBCOPY control statements and load-module handling used in this article.

Real-world recipes

Ship a PDS to a remote z/OS via FTP

The standard cross-system library move: unload to sequential, FTP in binary, reload on the target. Cross-system copies via DFDSS or XMIT are alternatives, but FTP is the universally available path.

Step 1 — unload locally:

text
//UNLOAD   JOB (ACCT),'ALICE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=ALICE.COBOL.SOURCE,DISP=SHR
//OUTDD    DD DSN=ALICE.COBOL.UNLOAD,
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(50,10)),
//             DCB=(RECFM=U,BLKSIZE=32760),
//             UNIT=SYSDA
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*

Step 2 — FTP transfer (see the ftp cheat sheet for details):

text
TYPE B
SITE RECFM=U BLKSIZE=32760 SPACE=(CYL,(50,10))
PUT 'ALICE.COBOL.UNLOAD' 'ALICE.COBOL.UNLOAD'
QUIT

Step 3 — reload on the remote system:

text
//RELOAD   JOB (ACCT),'ALICE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//ALLOC    EXEC PGM=IEFBR14
//NEWPDS   DD DSN=ALICE.COBOL.SOURCE,
//             DISP=(NEW,CATLG,DELETE),
//             SPACE=(CYL,(20,5,30)),
//             DCB=(RECFM=FB,LRECL=80,BLKSIZE=27920),
//             UNIT=SYSDA
//RELOAD   EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=ALICE.COBOL.UNLOAD,DISP=SHR
//OUTDD    DD DSN=ALICE.COBOL.SOURCE,DISP=OLD
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*

Promote selected members from DEV to PROD

A typical release pipeline: a subset of source members has been signed off and needs to land in a production library, replacing any existing copies. The R on the SELECT tuples ensures the production members are overwritten even though INDD lacks the R modifier.

text
//PROMOTE  JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//DEVLIB   DD DSN=ALICE.DEV.LOAD,DISP=SHR
//PRODLIB  DD DSN=ALICE.PROD.LOAD,DISP=OLD
//SYSIN    DD *
  COPYGRP INDD=DEVLIB,OUTDD=PRODLIB
    SELECT MEMBER=(                          -
                   (BILLPGM,BILLPGM,R),      -
                   (REPORT01,REPORT01,R),    -
                   (DAILYRPT,DAILYRPT,R)     -
                  )
/*

Compress and refresh a development PDS

The PDS has had a thousand member edits over the last month. Free space is fragmented; allocations are starting to fail with D37. Compress in place — no data is moved between datasets, so no SPACE allocation is needed beyond what the PDS already has.

text
//COMPRESS JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//SYSUT3   DD UNIT=SYSDA,SPACE=(CYL,(5,2))
//SYSUT4   DD UNIT=SYSDA,SPACE=(CYL,(5,2))
//MYPDS    DD DSN=ALICE.DEV.SOURCE,DISP=OLD
//SYSIN    DD *
  COMPRESS OUTDD=MYPDS
/*

A safer alternative is "compress by copy": unload to a fresh PDS, then rename. This avoids any in-place risk and gives a free backup of the dataset for the cost of one JCL step:

text
//COMPCOPY JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//ALLOC    EXEC PGM=IEFBR14
//NEWPDS   DD DSN=ALICE.DEV.SOURCE.NEW,
//             DISP=(NEW,CATLG,DELETE),
//             LIKE=ALICE.DEV.SOURCE,
//             UNIT=SYSDA
//COPY     EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=ALICE.DEV.SOURCE,DISP=SHR
//OUTDD    DD DSN=ALICE.DEV.SOURCE.NEW,DISP=OLD
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*
//*  Manual swap: delete the old, rename the new — see IDCAMS DELETE/RENAME

Merge two libraries with rename to avoid collisions

Two teams have built tooling in separate libraries with overlapping member names. Pull both into a single library, renaming team-B's members with a B suffix so neither set is lost.

text
//MERGE    JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//LIBA     DD DSN=ALICE.TEAM.A.UTILS,DISP=SHR
//LIBB     DD DSN=ALICE.TEAM.B.UTILS,DISP=SHR
//COMBINED DD DSN=ALICE.COMBINED.UTILS,DISP=OLD
//SYSIN    DD *
  COPY INDD=LIBA,OUTDD=COMBINED
  COPY INDD=LIBB,OUTDD=COMBINED
    SELECT MEMBER=(                          -
                   (UTIL01,UTIL01B,R),       -
                   (UTIL02,UTIL02B,R),       -
                   (HELPER,HELPERB,R)        -
                  )
/*

Back up a PDSE alias group before a refresh

The load library contains one module with three entry-point aliases. Snapshot the existing version to a versioned backup library before the new build overlays it, so a roll-back can copy back the original group atomically.

text
//BACKUP   JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//PRODLIB  DD DSN=ALICE.PROD.LOAD,DISP=SHR
//BACKLIB  DD DSN=ALICE.BACKUP.LOAD.V20260525,
//             DISP=(NEW,CATLG,DELETE),
//             LIKE=ALICE.PROD.LOAD,
//             DSNTYPE=LIBRARY,
//             UNIT=SYSDA
//SYSIN    DD *
  COPYGRP INDD=PRODLIB,OUTDD=BACKLIB
    SELECT MEMBER=(MAINPGM)
/*

MAINPGM and its aliases land in BACKLIB as a single group; restoring is the same statement with INDD and OUTDD swapped.

Selective copy with EXCLUDE for production cleanup

A development library has accumulated scratch and $TEMP members that must not reach production. Use EXCLUDE to copy everything except those, in one pass.

text
//CLEAN    JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//DEVLIB   DD DSN=ALICE.DEV.COPYLIB,DISP=SHR
//PRODLIB  DD DSN=ALICE.PROD.COPYLIB,DISP=OLD
//SYSIN    DD *
  COPY INDD=((DEVLIB,R)),OUTDD=PRODLIB
    EXCLUDE MEMBER=(                          -
                    $TEMP01,                  -
                    $TEMP02,                  -
                    SCRATCH,                  -
                    TEST01,                   -
                    TEST02,                   -
                    DEBUGCFG                  -
                   )
/*

One-step unload to a USS file via BPXBATCH wrapper

When the next leg of the pipeline lives in z/OS UNIX, write the unload to a USS file directly by allocating it via PATH= rather than a sequential dataset. The unload format is preserved; downstream tools (scp, rsync, Git) move it like any other file.

text
//UNLDUSS  JOB (ACCT),'ALICEJ01',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP1    EXEC PGM=IEBCOPY,REGION=0M
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=ALICE.COBOL.SOURCE,DISP=SHR
//OUTDD    DD PATH='/u/alice/exports/cobol.unload',
//             PATHOPTS=(OWRONLY,OCREAT,OTRUNC),
//             PATHMODE=(SIRUSR,SIWUSR,SIRGRP),
//             FILEDATA=BINARY,
//             RECFM=U,
//             LRECL=0,
//             BLKSIZE=32760
//SYSIN    DD *
  COPY INDD=INDD,OUTDD=OUTDD
/*

The reload side accepts the file the same way — switch OUTDD and INDD and point at the USS path with PATH=...,PATHOPTS=ORDONLY.

Verify what got copied

The SYSPRINT report from any IEBCOPY job names every member acted on. Capture and grep it for promotion audit trails:

bash
# After downloading SYSPRINT via FTP or Zowe to a workstation
grep "MEMBER" sysprint.txt | head -20

Output:

ini
IEB1135I MEMBER NAME = BILLPGM        REPLACED IN OUTPUT DATA SET
IEB1135I MEMBER NAME = REPORT01       REPLACED IN OUTPUT DATA SET
IEB1135I MEMBER NAME = DAILYRPT       REPLACED IN OUTPUT DATA SET
IEB154I  3 OF 3 MEMBERS WERE COPIED

Return codes to check in JCL IF blocks:

RCMeaning
0All members processed successfully.
4One or more members were not replaced (no R on INDD or tuple).
8I/O error or DD-level problem; check SYSPRINT.
12Catastrophic — input or output unusable.
16Out of space (B37/D37/E37) or RACF denied access.