Copybooks, COPY, and REPLACE
|
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), GnuCOBOL 3.2 (28 July 2023, the latest
release) and IBM Enterprise COBOL for z/OS 6.5, and was checked on 2026-10-20. COPY and REPLACE are old features of
the language, so everything on this page behaves the same in all three unless a difference is called out. The portable
examples were compiled with cobc -x -Wall (GnuCOBOL 3.2.0) without a warning and run; the output and the diagnostics
are real. Everything that talks about JCL, the SYSLIB DD and the COPYLOC option is z/OS only and was not run here; it
is checked against the Enterprise COBOL 6.5 documentation.
A copybook is a piece of COBOL source kept in a library and pulled into a program by the COPY statement before the
program is compiled. Most often it is a record layout. In the running Circulation scenario of this guide, the three
files USERID.CIRC.BOOKMAST, USERID.CIRC.MEMBMAST and USERID.CIRC.LOANTRAN are each described exactly once, in the
copybooks BOOKREC, MEMBREC and LOANREC, and every program that touches those files copies the layout instead of
retyping it. That is the point of the feature: when the layout changes, one member changes and every program is recompiled
against it. Shared layouts are the backbone of a COBOL system; the book layout in
Data Division and Level Numbers is the one shown
here as a copybook, and the way COPY is handled by the compiler is part of the pipeline described in
Compiling, Linking and Language Environment.
The COPY statement
COPY is a compiler-directing statement: it is not executed, it is processed by the compiler (or a preprocessing phase)
which replaces the whole statement, from the word COPY to the closing period, with the text of the library member. In
the words of the Language Reference, compiling source that contains COPY statements is logically equivalent to
processing all of them first and compiling the resulting text. The general form is:
| Part | Meaning |
|---|---|
|
the member to copy. |
|
where the member lives. |
|
keep the copied text out of the source listing. Enterprise COBOL honors it; GnuCOBOL accepts it and ignores it. |
|
change parts of the copied text while copying; see REPLACING. |
|
the period is mandatory. |
The rules that matter in practice:
-
The statement must be preceded by a space and ended by a separator period, even when it sits inside an
IFor at the end of a sentence that already has one. The period belongs toCOPY, and it does not end the enclosing statement. -
COPYmay appear wherever the copied text would be valid: under anFD, inWORKING-STORAGE, inside a paragraph in thePROCEDURE DIVISION, even in the middle of a record description to add subordinate items. -
Copybooks may contain
COPYstatements. GnuCOBOL repeats the expansion until none is left. Enterprise COBOL allows nesting too, and a copy member may be named only once in a chain of nested copies (no recursion). If more than oneCOPYin a chain has aREPLACINGphrase, Enterprise COBOL reportsIGYLI0078-Sand discards the second one (z/OS; not run here). -
Copied text must be valid reference format (see Reference Format: Fixed and Free). Text is placed in the same area of the program as it has in the library, so a copybook is written with the same columns as the program that uses it.
-
On Enterprise COBOL,
text-nameis 1 to 30 characters from letters, digits and hyphen, neither first nor last a hyphen, no underscore. When members are searched in a PDS or PDSE, only the first eight characters identify the member, which is why copybook names such asBOOKRECare short. In z/OS UNIX directories every character counts and those restrictions do not apply.
The Circulation copybooks
The three layouts are the same ones used by every page of this guide (a column-7 asterisk marks a comment line in the copybook; copybooks use fixed format here):
* BOOKREC - Circulation book master record (60 bytes)
01 BOOK-RECORD.
05 BOOK-ID PIC 9(6).
05 BOOK-ISBN PIC X(13).
05 BOOK-TITLE PIC X(30).
05 BOOK-COPIES PIC 9(3).
05 BOOK-PRICE PIC 9(4)V99.
05 BOOK-STATUS PIC X.
88 BOOK-ACTIVE VALUE "A".
88 BOOK-WITHDRAWN VALUE "W".
05 FILLER PIC X(1).
* MEMBREC - Circulation member master record (60 bytes)
01 MEMBER-RECORD.
05 MEMBER-ID PIC 9(6).
05 MEMBER-NAME PIC X(30).
05 MEMBER-JOINED PIC 9(8).
05 MEMBER-FINES PIC S9(5)V99.
05 MEMBER-STATUS PIC X.
88 MEMBER-ACTIVE VALUE "A".
88 MEMBER-SUSPENDED VALUE "S".
05 FILLER PIC X(8).
* LOANREC - Circulation loan transaction record (30 bytes)
01 LOAN-RECORD.
05 LOAN-TYPE PIC X.
88 LOAN-CHECKOUT VALUE "O".
88 LOAN-RETURN VALUE "R".
05 LOAN-MEMBER-ID PIC 9(6).
05 LOAN-BOOK-ID PIC 9(6).
05 LOAN-DATE PIC 9(8).
05 FILLER PIC X(9).
BOOK-COPIES counts the copies currently on the shelf: a checkout subtracts one and a return adds one, so a book whose
count is zero has every copy on loan. MEMBER-FINES is signed zoned decimal, which keeps the sample files readable text;
a z/OS master file would normally hold such an amount as packed decimal (COMP-3, see
USAGE and Numeric Representation).
LOAN-DATE is YYYYMMDD. A program copies the layout under the FD of the file it reads:
IDENTIFICATION DIVISION.
PROGRAM-ID. LISTBOOK.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT BOOK-FILE ASSIGN TO "BOOKMAST"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD BOOK-FILE.
COPY BOOKREC.
WORKING-STORAGE SECTION.
01 WS-EOF PIC X VALUE "N".
88 AT-END VALUE "Y".
PROCEDURE DIVISION.
MAIN-PARA.
OPEN INPUT BOOK-FILE
PERFORM UNTIL AT-END
READ BOOK-FILE
AT END SET AT-END TO TRUE
NOT AT END
DISPLAY BOOK-ID " " BOOK-TITLE " " BOOK-STATUS
END-READ
END-PERFORM
CLOSE BOOK-FILE
STOP RUN.
Compiled with cobc -x -Wall -I. listbooks.cob (the copybooks are in the same directory), no warnings, output:
000001 DATA STRUCTURES A
000002 COBOL BASICS A
000003 BATCH PROCESSING A
000004 OLD LEDGERS W
The same program on z/OS is identical; only the ASSIGN clause and the JCL differ (see
File Concepts, SELECT and FD). The IBM Programming Guide
gives the same idea with a different member: a library entry holding file-description clauses is retrieved by
FD FILEA followed by COPY CFILEA., and the listing marks the copied lines with a C.
REPLACING
REPLACING changes the library text for this one COPY; the member itself is not modified. Each operand-1 is compared
with the library text word by word, in the order written, and the first match is replaced by the corresponding
operand-2. A text word is a COBOL word, literal or separator, so matching is always against whole words unless
LEADING or TRAILING is used.
| Form | What it matches |
|---|---|
|
a sequence of one or more text words, up to the line end. The replacement may be several words, or empty ( |
|
a single identifier, literal or COBOL word, without the |
|
library words that start with the characters of |
|
library words that end with them |
Four rules explain nearly every surprise:
-
Whole words.
==BOOK== BY ==TITLE==does not changeBOOK-ID: the wordBOOK-IDis not the wordBOOK. To change the prefix of a name, useLEADINGor the:TAG:convention below. -
Partial words are one word.
partial-word-1is one text word, andpartial-word-2is zero or one text word. Neither can be a literal. -
Pictures. A
PICTUREstring is matched as part of its clause. To change one, put the entire clause, with the wordPICorPICTURE, in the pseudo-text. To change a level number without touching the9(6)in a picture, the Language Reference givesCOPY xxx REPLACING ==(01)== BY ==(01)== == 01 == BY == 05 ==.: the first pair protects the(01), the second changes a bare level number. -
Comments and blanks in the library text, the pseudo-text and the partial words are ignored when matching, and comments are copied unchanged.
The :TAG: convention is the portable way to replace a part of a name. The library text contains a dummy operand
delimited by colons (parentheses also work, but colons are recommended because parentheses are used for subscripts), and
the COPY replaces the whole delimited word:
* STATREC - run counters; the prefix is supplied by :TAG:
01 :TAG:-STATS.
05 :TAG:-READ PIC 9(4) VALUE ZERO.
05 :TAG:-REJECTED PIC 9(4) VALUE ZERO.
The next program copies the Circulation book layout twice with different prefixes (LEADING), the counters twice with
different tags, and the member layout with a whole-word and a TRAILING replacement:
IDENTIFICATION DIVISION.
PROGRAM-ID. REPLDEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
* Two copies of the same layout: LEADING changes the prefix
COPY BOOKREC REPLACING LEADING ==BOOK-== BY ==OLD-==.
COPY BOOKREC REPLACING LEADING ==BOOK-== BY ==NEW-==.
* The same counters twice: the :TAG: convention
COPY STATREC REPLACING ==:TAG:== BY ==BOOK==.
COPY STATREC REPLACING ==:TAG:== BY ==MEMBER==.
* Whole-word replacement and TRAILING
COPY MEMBREC REPLACING ==MEMBER-FINES== BY ==MEMBER-BALANCE==
TRAILING ==-ID== BY ==-KEY==.
PROCEDURE DIVISION.
MAIN-PARA.
MOVE 1 TO OLD-ID
MOVE 2 TO NEW-ID
MOVE "COBOL BASICS" TO NEW-TITLE
MOVE 3 TO BOOK-READ
MOVE 1 TO MEMBER-REJECTED
MOVE 101 TO MEMBER-KEY
MOVE 12.50 TO MEMBER-BALANCE
DISPLAY OLD-ID " " NEW-ID " " NEW-TITLE
DISPLAY BOOK-READ " " MEMBER-REJECTED
DISPLAY MEMBER-KEY " " MEMBER-BALANCE
STOP RUN.
It compiles without a warning (also with -std=ibm) and prints:
000001 000002 COBOL BASICS
0003 0001
000101 +00012.50
Seeing the expanded text removes the guesswork. cobc -E replacing.cob writes the source after COPY and REPLACE have
been processed (the listing below is shortened to the first and last copy; cobc -E drops comment lines and compresses
blanks, and its #line markers show which file each line came from):
01 OLD-RECORD.
05 OLD-ID PIC 9(6).
05 OLD-ISBN PIC X(13).
...
01 BOOK-STATS.
05 BOOK-READ PIC 9(4) VALUE ZERO.
05 BOOK-REJECTED PIC 9(4) VALUE ZERO.
...
01 MEMBER-RECORD.
05 MEMBER-KEY PIC 9(6).
05 MEMBER-NAME PIC X(30).
05 MEMBER-JOINED PIC 9(8).
05 MEMBER-BALANCE PIC S9(5)V99.
Note that LEADING ==BOOK-== also turned BOOK-RECORD into OLD-RECORD and BOOK-ACTIVE into OLD-ACTIVE: every word that
starts with the characters is changed. Both compilers expose the same facility for finding such surprises: GnuCOBOL with
-E, Enterprise COBOL with the source listing, where copied lines are marked (z/OS; not run here).
REPLACE
REPLACE is the same text substitution applied to the program itself, not only to copied text. It is a
compiler-directing statement too: it ends with a period and may appear wherever a character string may. A REPLACE
statement stays in effect from where it is written until the next REPLACE statement or the end of the separately
compiled program. REPLACE OFF. cancels it. The compiler processes COPY first and REPLACE second, so a REPLACE also
acts on text that came from a copybook, and a REPLACE cannot contain a COPY. Text produced by a REPLACE must not
contain another REPLACE.
| Aspect | COPY ... REPLACING |
REPLACE |
|---|---|---|
Acts on |
the text of one copied member |
the program text that follows, including copied text |
Lasts |
that |
until the next |
Operands |
pseudo-text, identifier, literal, word, |
pseudo-text, |
Typical use |
a layout reused under several names |
a program-wide constant or name |
IDENTIFICATION DIVISION.
PROGRAM-ID. REPLSTMT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-LOANS PIC 9(2) VALUE 3.
01 MAX-LOANS PIC 9(2) VALUE 5.
PROCEDURE DIVISION.
MAIN-PARA.
REPLACE ==MAX-LOANS== BY ==3==
==LOAN-LIMIT-MSG== BY =="LOAN LIMIT REACHED"==.
IF WS-LOANS >= MAX-LOANS
DISPLAY LOAN-LIMIT-MSG " (" MAX-LOANS ")"
END-IF
.
REPLACE OFF.
DISPLAY "MAX-LOANS data item: " MAX-LOANS
STOP RUN.
The data item MAX-LOANS really exists and holds 5, but while the first REPLACE is active the word is replaced by the
literal 3; after REPLACE OFF. it is an ordinary name again. Output of the compiled program (no warnings):
LOAN LIMIT REACHED (3)
MAX-LOANS data item: 05
A constant defined with REPLACE is invisible to the debugger and to the data-name cross-reference, because it never
becomes a data item; for values that belong to the program prefer a VALUE clause or a condition name, and keep REPLACE
for what really is text. The GnuCOBOL manual also documents REPLACE ALSO, which adds replacements to the ones already in
effect instead of discarding them, and REPLACE LAST OFF, which removes the most recent one. The Enterprise COBOL 6.5
Language Reference describes only the plain form and REPLACE OFF, so code meant for both compilers should not use the
two extra forms. GnuCOBOL 3.2 accepts them:
IDENTIFICATION DIVISION.
PROGRAM-ID. REPLALSO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-A PIC 9 VALUE 0.
01 WS-B PIC 9 VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
REPLACE ==ONE== BY ==1==.
REPLACE ALSO ==TWO== BY ==2==.
MOVE ONE TO WS-A
MOVE TWO TO WS-B
DISPLAY WS-A WS-B
.
REPLACE LAST OFF.
REPLACE OFF.
STOP RUN.
12
Compiler directives such as >>DEFINE and >>IF, which choose which text is compiled, are a different mechanism and are
covered in
Compiler Directives and Conditional Compilation.
Where copybooks are found
The statement names a member; the environment decides which libraries are searched. The two compilers describe the same idea with different tools.
| Question | Enterprise COBOL on z/OS | GnuCOBOL 3.2 |
|---|---|---|
The default library |
the concatenation of data sets in the |
the directories below, in order |
An explicit library name ( |
|
the directory named |
Extra locations |
the |
|
GnuCOBOL’s own description is that COB_COPY_DIR is the variable that IBM mainframe programmers will recognize as SYSLIB.
Its search order for COPY XXXXXXXX. is: the folder named as the library, the folder of the program being compiled, the
-I folders, the COBCPY folders, then COB_COPY_DIR. In each folder the names XXXXXXXX.CPY, .CBL, .COB, .cpy,
.cbl, .cob and the bare name are tried in that order. Enterprise COBOL, when searching a z/OS UNIX directory for a
member that is not written as a literal, tries the extensions .cpy, .CPY, .cbl, .CBL, .cob and .COB with upper
and lower case versions of the name. That is why BOOKREC.cpy works for both.
On z/OS: SYSLIB
On z/OS the copybooks of the Circulation system live in a partitioned data set, one member per copybook (z/OS; not run
here). The compile step names the library in the SYSLIB DD; several libraries are concatenated by leaving the name off
the following DD statements, and they are searched in order:
//SYSLIB DD DISP=SHR,DSN=USERID.CIRC.COPYLIB
// DD DISP=SHR,DSN=USERID.SHARED.COPYLIB
This DD is what makes COPY BOOKREC. work; the compiler has no other way to find the member. The libraries must be on
direct-access storage and cannot be z/OS UNIX files when compiling with JCL or under TSO. The compile procedures and the
other data sets of the compile step are covered in
JCL for COBOL Programmers.
The COPYLOC compiler option, available from Enterprise COBOL 6.2 with the PTF for APAR PI91584, adds more places to
look. It is searched last, and it may be repeated, mixing data sets and directories:
COPYLOC(MYLIB,DSN(USERID.CIRC.COPYLIB1))
COPYLOC(MYLIB,PATH('/u/userid/copylib1'))
The Language Reference topic that fixes the order of all of this is the copy member search order.
On GnuCOBOL: -I, COBCPY and COB_COPY_DIR
All of the following were run in a container with the copybooks in a copylib directory next to the program. Without any
help, the compiler cannot find the member and every later reference fails too (this example is deliberately wrong):
$ cobc -x -Wall useit.cob
useit.cob:5: error: BOOKREC: No such file or directory
3 | DATA DIVISION.
4 | WORKING-STORAGE SECTION.
5 > COPY BOOKREC.
6 | PROCEDURE DIVISION.
7 | MOVE 1 TO BOOK-ID
useit.cob:7: error: 'BOOK-ID' is not defined
Each of the following then compiles without warnings and prints BOOK-ID 000001:
cobc -x -Wall -I copylib useit.cob
COB_COPY_DIR=copylib cobc -x -Wall useit.cob
COBCPY=copylib cobc -x -Wall useit.cob
The library name on the statement works too, because the directory named by the library is searched first:
COPY BOOKREC OF "copylib".
The environment variables are described in the same guide.
A Makefile or CI job normally sets -I once for the whole project, which is the equivalent of the shared SYSLIB
concatenation: see
DevOps and CI/CD for COBOL.
Naming and case
Name copybook files after the member, with the extension .cpy: BOOKREC.cpy, MEMBREC.cpy, LOANREC.cpy. A member of
a PDS has no extension, which is why the name is limited to eight significant characters there; on a file system the
extension is how both compilers recognize it, and .cpy is the first one they try. Case is the usual trap on Linux and
macOS, whose file systems are case-sensitive in most configurations: COPY is case-sensitive on UNIX. With the file called
BOOKREC.cpy, a program that says COPY bookrec. fails (this example is deliberately wrong):
$ cobc -x -Wall -I copylib uselow.cob
uselow.cob:5: error: bookrec: No such file or directory
5 | COPY bookrec.
The option -ffold-copy=upper (or lower) makes the compiler fold every copybook name to one case, and >>SET can switch
folding on and off inside the source. With -ffold-copy=upper the same program compiles and runs. The simplest policy is
to name the files and the statements in upper case, as on the mainframe.
Pitfalls with real diagnostics
Each of the following was compiled with cobc -x -Wall; the messages are real. The first three are deliberately wrong.
The same copybook twice. Copying LOANREC twice defines LOAN-RECORD and every field twice. GnuCOBOL warns about the
redefinition, and any reference to one of the names is then ambiguous. Use REPLACING (as in the example above) to give the second
copy other names.
LOANREC.CPY:2: warning: redefinition of 'LOAN-RECORD' [-Wredefinition]
2 > 01 LOAN-RECORD.
LOANREC.CPY:2: note: 'LOAN-RECORD' previously defined here [-Wredefinition]
No period after COPY. The next word is taken as part of the statement:
noperiod.cob:6: error: syntax error, unexpected Word or Literal, expecting .
5 | COPY LOANREC
6 > PROCEDURE DIVISION.
noperiod.cob:7: error: PROCEDURE DIVISION header missing
Replacing part of a word. COPY BOOKREC REPLACING ==BOOK== BY ==TITLE==. replaces nothing, because no library word
is exactly BOOK, and the compiler says so only when a name is used:
wholeword.cob:7: error: 'TITLE-ID' is not defined
7 > MOVE 1 TO TITLE-ID
Two more problems are not diagnosed at all. A REPLACING that matches nothing is silent. And on z/OS a copybook edited
after a program was compiled does not change the object module: the program keeps the old layout until it is recompiled.
Treat a layout change as a change to every program that copies it; the practice is discussed in
Coding Standards and Best Practices.
IEBCOPY and the copy library
A mainframe copy library is a partitioned data set (PDS) or PDSE, and the utility that copies, merges and reorganizes the
members of such data sets is IEBCOPY. A commonly shown job step uses it to build or refresh the copy library, for
example copying the members of one library into another with a control statement of the form
COPY OUTDD=OUT,INDD=IN (z/OS; not run here). The step only fills the library. It is the SYSLIB DD of the compile
step, shown above, that makes the compiler read it: COPY BOOKREC. never mentions IEBCOPY, and a library that no
SYSLIB or COPYLOC points to is invisible to the compiler. The utility and its control statements are documented in
the DFSMSdfp Utilities manual; the other dataset utilities are covered in
Datasets and Mainframe Utilities. Where the book
by Tom Taulli presents the IEBCOPY step as "the copy library", read it as the place where members are stored; the
compile step is where they are resolved.
Where the book is outdated
The book describes copy members and the library step in the vocabulary of a mainframe compile job and does not cover what
changed afterwards. Three things have moved: copy members can now also be searched in z/OS UNIX directories (with cob2,
-I and the SYSLIB environment variable) and through COPYLOC, so a copybook no longer has to be a PDS member; the
COPY ... REPLACING rules were tightened and documented in detail, including the single-REPLACING-per-chain rule and
the partial-word forms; and on a workstation the equivalent of the library is a directory given to cobc -I, with
COB_COPY_DIR or COBCPY as the environment form.