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.
//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
| DD | Purpose |
|---|---|
SYSPRINT | Required. Receives the IEBCOPY report — counts of members copied, replaced, skipped. |
SYSIN | Required. Holds the control statements (COPY, SELECT, EXCLUDE, COPYGRP, COMPRESS). |
SYSUT3 | Optional work file used when input and output BLKSIZE differ. |
SYSUT4 | Optional work file used during directory rebuild. |
SYSUT1 | Legacy name commonly used as the input DD. |
SYSUT2 | Legacy name commonly used as the output DD. |
ddname | Any 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:
| Statement | Purpose |
|---|---|
COPY | Begin a copy group; specifies INDD/OUTDD pair(s). |
SELECT | List members to copy from the most recent COPY. |
EXCLUDE | List members to skip from the most recent COPY. |
COPYGRP | PDSE-only — copy a member alias group as a unit. |
COMPRESS | PDS-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.
//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:
//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.
//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:
//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.
//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.
//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.
//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.
//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) andBLKSIZE=32760are required for the unload format. Any other DCB causes the reload step to fail withIEB1071I 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.
//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:
//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.
//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:
| Aspect | PDS | PDSE |
|---|---|---|
| Free space | Accumulates dead members until COMPRESS | Reclaimed automatically |
| Directory size | Fixed at allocation (SPACE=(...,dir)) | Auto-extends |
| Concurrent updates | One updater at a time | Multiple updaters, member-level locking |
| Member aliases | One name per stored copy | Many aliases per stored copy (use COPYGRP) |
| COMPRESS | Required periodically | Rejected with IEB1099I |
| DSNTYPE | DSNTYPE=PDS or omitted with SPACE=…,dir | DSNTYPE=LIBRARY |
| Cross-system copy | Unload/reload preserves directory format | Same — 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
- Forgetting
,Rand 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 showsMEMBER NOT REPLACEDlines; always check the SYSPRINT count. - Using
COMPRESSon a PDSE — fails withIEB1099I. PDSE manages its own free space; no action needed. - Open ISPF Edit on a PDS while running
COMPRESS— job ABENDs with913-38(RACF/enqueue). Close all editors and any active jobs holding the dataset before the step starts. - Wrong DCB on an unload dataset — anything other than
RECFM=U,BLKSIZE=32760produces an unreadable unload. The reload step fails withIEB1071I. SELECTon a PDSE with aliases — copies the named member without its aliases. UseCOPYGRPfor any PDSE that holds load modules with multiple entry-point names.- Directory full — appears as
IEC031I D37on 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. - Member name collision across multiple INDDs — without
,Ron the INDD spec, the first occurrence wins. Order your INDD list intentionally if libraries have overlapping names. - 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.
- 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.
- Forgetting
DISP=OLDfor COMPRESS —DISP=SHRis 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:
//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):
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:
//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.
//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.
//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:
//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.
//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.
//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.
//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.
//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:
# After downloading SYSPRINT via FTP or Zowe to a workstation
grep "MEMBER" sysprint.txt | head -20
Output:
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:
| RC | Meaning |
|---|---|
| 0 | All members processed successfully. |
| 4 | One or more members were not replaced (no R on INDD or tuple). |
| 8 | I/O error or DD-level problem; check SYSPRINT. |
| 12 | Catastrophic — input or output unusable. |
| 16 | Out of space (B37/D37/E37) or RACF denied access. |