File Concepts: SELECT and FD

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), where the file-control entry, the file description and the OPEN and CLOSE statements come from COBOL 85 and earlier, record locking was added by COBOL 2002 and LINE SEQUENTIAL organization and DELETE FILE by COBOL 2023; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which every complete program on it was compiled with cobc -x -Wall and run on 2026-10-20 (programs that differ under -std=ibm were run both ways). The z/OS JCL on the page is not run here; it is checked against the Language Reference and the Programming Guide. The examples use the Circulation public-library scenario: the book master BOOKMAST, the member master MEMBMAST and the loan transactions LOANTRAN, whose record layouts are the copybooks BOOKREC, MEMBREC and LOANREC. Their sample data are line-sequential text files with one record per line.

A COBOL program never mentions a disk, a directory or a data set name in its statements. It names a file with a file-name that two entries describe: a SELECT entry in the ENVIRONMENT DIVISION that says how the file is organized, accessed and found, and an FD entry in the DATA DIVISION that says what its records look like. The OPEN statement then connects the two descriptions to a real file. This page covers those four things in that order, because each one fixes a part of the contract that the statements on the Sequential Files, Indexed Files and VSAM and Relative Files pages rely on. The status codes that every file statement leaves behind are on File Status Codes, and what to do with them is on Error Handling and Declaratives.

Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapter 5) introduces SELECT and FD for a customer file, and the idea is still current. Three details are wrong, and the correct rules are these. A file is opened with OPEN INPUT, not "OPEN READ". The FILE STATUS item is a separate two-character item in WORKING-STORAGE, not a field declared inside the record group (which would make it part of the data of the file). And a PC-style name such as ASSIGN TO "Customers.dat" with LINE SEQUENTIAL is how a GnuCOBOL program names a file on a PC; it is not how an Enterprise COBOL batch program names a data set. On z/OS the assignment name is a name of at most eight characters that stands for a JCL DD statement, and Enterprise COBOL supports line-sequential files only for z/OS UNIX files. The sections below show both worlds side by side.

The file model

Four words are used loosely in everyday talk and precisely by the language.

Term Meaning In the Circulation scenario

Logical record

The unit that one READ or WRITE moves between the file and the program: the record described by an FD entry

One book, one member or one loan transaction; BOOK-RECORD is 60 bytes

Physical record or block

The unit that the operating system transfers to and from the device; it holds one or more logical records and, for variable-length records, control bytes

On z/OS, a block of many 60-byte records on disk; with GnuCOBOL’s line-sequential files there is no block, only lines

File

A named collection of logical records, with an organization that was fixed when the file was created

BOOKMAST

Data set

The z/OS word for a file that the system catalogs and allocates; a QSAM sequential data set, a PDS member or a VSAM cluster

USERID.CIRC.BOOKMAST

The organization is the permanent logical structure of the file and cannot be changed after the file has been created. The access mode is how one program reads or writes it. Four organizations exist in the language.

ORGANIZATION Structure Access modes allowed Notes

SEQUENTIAL

Records follow one another in the order they were written; each one except the first has a predecessor

Sequential only

Fixed or variable length; the default when the clause is omitted. On z/OS it is a QSAM data set, or a VSAM ESDS when the assignment name starts with AS-

LINE SEQUENTIAL

Text lines; each record ends with a delimiter that is not part of the record

Sequential only

The text-file organization. Standardised in COBOL 2023, a GnuCOBOL staple, and in Enterprise COBOL limited to z/OS UNIX files

INDEXED

Records are found through an index built on a unique prime RECORD KEY, plus optional alternate keys

Sequential, random, dynamic

On z/OS a VSAM KSDS (or VSAMDB); in GnuCOBOL a Berkeley DB or similar back end

RELATIVE

Record areas numbered 1, 2, 3 and so on; the position is the relative record number

Sequential, random, dynamic

On z/OS a VSAM RRDS

Records of a sequential file can be fixed or variable in length and carry no keys. The statements that are specific to each organization are on the three file pages named above; this page covers what they all share.

Program, FD, DD name and data set

The figure shows the chain from a statement to the bytes on disk. The program refers to the file-name BOOK-FILE. The FD of that file-name gives its record layout, and the SELECT entry gives its assignment name, here BOOKMAST. What the assignment name means is decided by the run-time system: on z/OS it is the name of a DD statement in the JCL of the job step, and in GnuCOBOL it is a key that is looked up in the environment and then taken as a path.

flowchart LR P["Program statements: OPEN, READ, WRITE, CLOSE BOOK-FILE"] --> FD["FD BOOK-FILE and its 01 record BOOK-RECORD"] FD --> SEL["SELECT BOOK-FILE ASSIGN TO BOOKMAST"] SEL --> Z1 SEL --> G1 subgraph ZOS["Enterprise COBOL on z/OS"] Z1["ddname BOOKMAST, folded to uppercase"] --> Z2["BOOKMAST DD statement in the job step"] Z2 --> Z3["Data set USERID.CIRC.BOOKMAST"] Z1 -. "no DD allocated" .-> Z4["Environment variable BOOKMAST holding DSN or PATH"] Z4 -.-> Z3 end subgraph GC["GnuCOBOL 3.2"] G1["Assignment name BOOKMAST"] --> G2{"Environment variable set?"} G2 -- "DD_BOOKMAST, else dd_BOOKMAST, else BOOKMAST" --> G3["Its value is the path"] G2 -- "none" --> G4["The name itself is the path"] G3 --> G5["COB_FILE_PATH is put in front of a relative path"] G4 --> G5 G5 --> G6["File on disk"] end

The figure follows the Language Reference rules for the assignment name and the GnuCOBOL manual and Programmer’s Guide rules for file-name mapping; the sections below test each arrow.

The SELECT entry

SELECT lives in the FILE-CONTROL paragraph of the INPUT-OUTPUT SECTION. Each entry starts with the file-name and then lists clauses ending with one period. The clause forms for Enterprise COBOL depend on the organization (the Language Reference has one format per organization), and GnuCOBOL accepts the union of them.

SELECT [OPTIONAL] file-name
    ASSIGN TO assignment-name
    [ORGANIZATION IS SEQUENTIAL | LINE SEQUENTIAL | INDEXED | RELATIVE]
    [ACCESS MODE IS SEQUENTIAL | RANDOM | DYNAMIC]
    [RECORD KEY IS key] [ALTERNATE RECORD KEY IS key [WITH DUPLICATES]]   (indexed)
    [RELATIVE KEY IS data-name]                                           (relative)
    [FILE STATUS IS status-item].

The clauses can follow the file-name in any order. A few rules from the Language Reference matter from the first program on.

  • file-name must be described by an FD (or SD, for sort and merge files) in the DATA DIVISION, must contain at least one letter and must be unique in the program.

  • ORGANIZATION defaults to SEQUENTIAL and ACCESS MODE to SEQUENTIAL. Always write both, because the line-sequential, indexed and relative organizations are not what a reader expects from a missing clause.

  • OPTIONAL is allowed only for files opened INPUT, I-O or EXTEND, and marks an input file that is not always there. Without it, a missing input file is an error; with it, the open succeeds with status 05 and the first READ reaches AT END (shown in the OPEN section below).

  • FILE STATUS IS names a two-character WORKING-STORAGE item that receives a code after every operation on the file. It is not optional in practice: without it the program has no way to find out why an OPEN failed.

  • RECORD KEY, ALTERNATE RECORD KEY and RELATIVE KEY are covered on the indexed and relative pages. The relative key must not be a field of the record.

A first complete program

The program lists the book master. It has one SELECT with a literal assignment name, one FD that pulls in the BOOKREC copybook, and the usual read loop, with FILE STATUS examined after OPEN and CLOSE.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. LISTBOOK.
      * List the book master: one SELECT, one FD, one read loop.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-FILE ASSIGN TO "BOOKMAST"
               ORGANIZATION IS LINE SEQUENTIAL
               ACCESS MODE IS SEQUENTIAL
               FILE STATUS IS WS-BOOK-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-FILE.
           COPY BOOKREC.
       WORKING-STORAGE SECTION.
       01  WS-BOOK-STATUS          PIC XX.
       01  WS-EOF                  PIC X VALUE "N".
           88  END-OF-BOOKS        VALUE "Y".
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN INPUT BOOK-FILE
           DISPLAY "OPEN status " WS-BOOK-STATUS
           PERFORM UNTIL END-OF-BOOKS
               READ BOOK-FILE
                   AT END SET END-OF-BOOKS TO TRUE
                   NOT AT END
                       DISPLAY BOOK-ID " " BOOK-TITLE " " BOOK-STATUS
               END-READ
           END-PERFORM
           CLOSE BOOK-FILE
           DISPLAY "CLOSE status " WS-BOOK-STATUS
           STOP RUN.

Compiled with cobc -x -Wall and run in the directory that holds BOOKMAST:

OPEN status 00
000001 DATA STRUCTURES                A
000002 COBOL BASICS                   A
000003 BATCH PROCESSING               A
000004 OLD LEDGERS                    W
CLOSE status 00

On z/OS the same SELECT is written with an unquoted ddname, and the job supplies the file. Compare the two ways of writing the assignment name before moving on.

           SELECT BOOK-FILE ASSIGN TO BOOKMAST
               ORGANIZATION IS SEQUENTIAL
               ACCESS MODE IS SEQUENTIAL
               FILE STATUS IS WS-BOOK-STATUS.
//LISTBOOK EXEC PGM=LISTBOOK
//STEPLIB  DD DISP=SHR,DSN=USERID.CIRC.LOADLIB
//BOOKMAST DD DISP=SHR,DSN=USERID.CIRC.BOOKMAST
//SYSOUT   DD SYSOUT=*

The JCL above is z/OS; not run here. The BOOKMAST DD statement is what the assignment name BOOKMAST refers to; the data set name never appears in the program. On z/OS the book master is a fixed-length QSAM data set, so the example uses ORGANIZATION IS SEQUENTIAL; the line-sequential organization would need a z/OS UNIX file. The Language Reference allows exactly two meanings for the name: the name on the DD statement for the file, or the name of an environment variable that holds file allocation information.

ASSIGN and the DD name

The ASSIGN clause connects the file-name to an external name. The two systems read the clause differently, and a program that is meant to run on both must be written with that in mind.

Enterprise COBOL on z/OS

The assignment name can be a user-defined word or an alphanumeric literal, and has an optional organization prefix.

Form File type Meaning

ASSIGN TO name or ASSIGN TO S-name

QSAM

name is the ddname; the S- organization field can be left out for QSAM

ASSIGN TO AS-name

VSAM sequential (ESDS)

The AS- field is required

ASSIGN TO name

VSAM indexed (KSDS), VSAM relative (RRDS), line sequential

The organization field must be omitted

ASSIGN TO VSAMDB-name

VSAMDB indexed

The prefix is required

ASSIGN TO label-name

any

A label- prefix ending in a hyphen documents the device class and has no effect on execution

The name is one to eight characters, starts with a letter and uses letters and digits (a literal may also use @, # and $). It cannot contain an underscore, and the compiler folds it to uppercase to form the ddname. This is the first thing a GnuCOBOL programmer must unlearn: BOOK_MASTER is a fine GnuCOBOL name and an illegal Enterprise COBOL assignment name. When there are several assignment names, only the first one has any effect.

The name is looked up in this order. It is first treated as a ddname. If the job step has no DD statement by that name, Enterprise COBOL treats it as the name of an environment variable, which is read at every OPEN. The variable must be named in uppercase and must hold a DSN(...) option for a QSAM or VSAM data set, or a PATH(...) option with an absolute path for a z/OS UNIX file. A line-sequential file accepts only PATH. If the variable is missing, malformed, or the dynamic allocation fails, the OPEN leaves file status 98.

export BOOKMAST="DSN(USERID.CIRC.BOOKMAST),SHR"
export NOTEFILE="PATH(/u/circ/notes.txt)"

The two lines are z/OS UNIX shell syntax in the style of the Language Reference; z/OS; not run here. The Language Reference recommends always coding a disposition (NEW, OLD, SHR or MOD) with DSN rather than relying on a system default.

GnuCOBOL

GnuCOBOL accepts three kinds of assignment name, and a compiler dialect decides how an unquoted word is read.

Form Example File that is opened

Literal

ASSIGN TO "data/BOOKMAST"

The literal is the file location string, after the environment lookup below

Data-name

ASSIGN USING WS-PATH (or ASSIGN TO WS-PATH)

The value of the item at the moment of OPEN, so the program can build the name with MOVE or STRING

EXTERNAL word

ASSIGN TO EXTERNAL BOOKEXT

The value of the environment variable BOOKEXT

The next program opens the same file the three ways and shows the first record’s title each time. The data file is in a data directory next to the program.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. ASSIGNFORMS.
      * Three ways to name the same data file in GnuCOBOL.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT LIT-FILE ASSIGN TO "data/BOOKMAST"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
           SELECT VAR-FILE ASSIGN USING WS-PATH
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
           SELECT EXT-FILE ASSIGN TO EXTERNAL BOOKEXT
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  LIT-FILE.
       01  LIT-LINE                PIC X(60).
       FD  VAR-FILE.
       01  VAR-LINE                PIC X(60).
       FD  EXT-FILE.
       01  EXT-LINE                PIC X(60).
       WORKING-STORAGE SECTION.
       01  WS-STATUS               PIC XX.
       01  WS-PATH                 PIC X(40).
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN INPUT LIT-FILE
           READ LIT-FILE
           DISPLAY "literal  : " LIT-LINE(20:30)
           CLOSE LIT-FILE
           MOVE "data/BOOKMAST" TO WS-PATH
           OPEN INPUT VAR-FILE
           READ VAR-FILE
           DISPLAY "variable : " VAR-LINE(20:30)
           CLOSE VAR-FILE
           DISPLAY "BOOKEXT" UPON ENVIRONMENT-NAME
           DISPLAY "data/BOOKMAST" UPON ENVIRONMENT-VALUE
           OPEN INPUT EXT-FILE
           READ EXT-FILE
           DISPLAY "EXTERNAL : " EXT-LINE(20:30)
           CLOSE EXT-FILE
           STOP RUN.
literal  : DATA STRUCTURES
variable : DATA STRUCTURES
EXTERNAL : DATA STRUCTURES

The output was identical with -std=ibm. DISPLAY ... UPON ENVIRONMENT-NAME and UPON ENVIRONMENT-VALUE set the environment variable from inside the program; in a shell script one would run export BOOKEXT=data/BOOKMAST before the program instead.

How GnuCOBOL turns a name into a path

When the compiler configuration has filename-mapping: yes (the default, and the setting in the default, -std=ibm and -std=mf dialects tried here), the run-time system does not use the assignment name as a path straight away. It looks for the first existing environment variable in this order, using the assignment name as the base:

  1. DD_ followed by the base, for example DD_BOOKS

  2. dd_ followed by the base, for example dd_BOOKS

  3. the base itself, for example BOOKS

The value of the variable that is found is the path and file name. If none exists, or if filename mapping is off (-fno-filename-mapping), the assignment name itself is the path. A relative path is then prefixed with the directory in the run-time setting COB_FILE_PATH (the current directory when it is not set), and an absolute path is used as it is. These are the same two ideas as the ddname and the DD statement on z/OS, with the shell standing in for the JCL: export DD_BOOKMAST=/data/circ/bookmast.dat is the GnuCOBOL spelling of a BOOKMAST DD statement.

The next program shows the order. BOOKS is a file that exists in the current directory; plain.dat, lower.dat and upper.dat are three other one-line files made for the test. The program adds one variable at a time and opens the file again, then points DD_BOOKS at a file that does not exist.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MAPDEMO.
      * Which file does ASSIGN TO "BOOKS" open? Set variables, look.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-FILE ASSIGN TO "BOOKS"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-FILE.
       01  BOOK-LINE               PIC X(40).
       WORKING-STORAGE SECTION.
       01  WS-STATUS               PIC XX.
       01  WS-NAME                 PIC X(8).
       01  WS-VALUE                PIC X(20).
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           DISPLAY "nothing set     : " WITH NO ADVANCING
           PERFORM 1000-SHOW-FILE
           MOVE "BOOKS" TO WS-NAME
           MOVE "plain.dat" TO WS-VALUE
           DISPLAY "BOOKS set       : " WITH NO ADVANCING
           PERFORM 2000-SET-AND-SHOW
           MOVE "dd_BOOKS" TO WS-NAME
           MOVE "lower.dat" TO WS-VALUE
           DISPLAY "dd_BOOKS added  : " WITH NO ADVANCING
           PERFORM 2000-SET-AND-SHOW
           MOVE "DD_BOOKS" TO WS-NAME
           MOVE "upper.dat" TO WS-VALUE
           DISPLAY "DD_BOOKS added  : " WITH NO ADVANCING
           PERFORM 2000-SET-AND-SHOW
           MOVE "DD_BOOKS" TO WS-NAME
           MOVE "missing.dat" TO WS-VALUE
           DISPLAY "DD_BOOKS missing: " WITH NO ADVANCING
           PERFORM 2000-SET-AND-SHOW
           STOP RUN.
       1000-SHOW-FILE.
           OPEN INPUT BOOK-FILE
           IF WS-STATUS = "00"
               READ BOOK-FILE
               DISPLAY BOOK-LINE
               CLOSE BOOK-FILE
           ELSE
               DISPLAY "OPEN failed, status " WS-STATUS
           END-IF.
       2000-SET-AND-SHOW.
           DISPLAY WS-NAME UPON ENVIRONMENT-NAME
           DISPLAY WS-VALUE UPON ENVIRONMENT-VALUE
           PERFORM 1000-SHOW-FILE.
nothing set     : FROM LITERAL FILE NAME BOOKS
BOOKS set       : FROM VARIABLE BOOKS
dd_BOOKS added  : FROM VARIABLE dd_BOOKS
DD_BOOKS added  : FROM VARIABLE DD_BOOKS
DD_BOOKS missing: OPEN failed, status 35

Each variable overrides the previous one, in the documented order, and the environment is read again at every OPEN. A variable that is set always wins: when DD_BOOKS points at missing.dat, GnuCOBOL does not fall back to the file called BOOKS, it fails with status 35. That is the behaviour of a DD statement that names a data set that does not exist, and it is usually what a batch job wants.

A relative path is also affected by COB_FILE_PATH. With BOOKMAST moved into a directory data, a program that has ASSIGN TO BOOKMAST found nothing in the current directory and, run as COB_FILE_PATH=data ./prog, opened data/BOOKMAST. When DD_BOOKMAST held an absolute path, COB_FILE_PATH was ignored (it was set to a directory that does not exist, and the file was still opened); when DD_BOOKMAST held the relative name BOOKMAST, COB_FILE_PATH=data was put in front of it.

export DD_BOOKMAST=/data/circ/bookmast.dat   # absolute: COB_FILE_PATH is not used
export DD_BOOKMAST=bookmast.dat              # relative: COB_FILE_PATH/bookmast.dat
export COB_FILE_PATH=/data/circ

The three lines are alternatives, not a script; the first two are two ways to write DD_BOOKMAST, and the last one sets the directory for relative names.

A portability pitfall: the label prefix

Enterprise COBOL lets the assignment name start with a documentation label such as UT-S- or WS-, and ignores it. GnuCOBOL applies a similar rule only under the IBM dialect, and that rule can swallow part of a data-name. The program below is deliberately wrong for -std=ibm: it writes the variable form as ASSIGN TO WS-PATH instead of ASSIGN USING WS-PATH.

The only change to the assignforms.cob program above is the second SELECT:

           SELECT VAR-FILE ASSIGN TO WS-PATH

Compiled with cobc -x -Wall -std=ibm, the program reports the problem as a warning and then reads nothing from that file:

assignforms.cob:11: warning: ASSIGN WS-PATH interpreted as 'PATH' [-Wadditional]
literal  : DATA STRUCTURES
variable :
EXTERNAL : DATA STRUCTURES

Under the IBM rules WS- is taken as a label and the assignment name becomes PATH, so the item WS-PATH is never used; the default dialect reads the word as a data-name. The fix is to use ASSIGN USING for the variable form (the assignforms.cob program above uses it and behaves the same in both dialects) and ASSIGN TO name only for the ddname form. A prefix is honoured only for an unquoted name under -std=ibm. With the unquoted ASSIGN TO S-BOOKMAST, cobc -std=ibm printed warning: ASSIGN S-BOOKMAST interpreted as 'BOOKMAST' [-Wadditional] and opened the file, the default dialect ended with status 35, and a quoted "S-BOOKMAST" ended with status 35 in both dialects even with BOOKMAST set in the environment. On GnuCOBOL write the plain name.

ORGANIZATION and ACCESS MODE together

The access mode says how the statements in the procedure division reach the records. It is limited by the organization.

Organization Access modes How a record is selected

SEQUENTIAL, LINE SEQUENTIAL

SEQUENTIAL only

By position: the order in which the records were written

INDEXED

SEQUENTIAL, RANDOM, DYNAMIC

Sequential: ascending record key. Random: the key placed in the RECORD KEY item. Dynamic: either, depending on the statement (READ, READ NEXT, START)

RELATIVE

SEQUENTIAL, RANDOM, DYNAMIC

Sequential: ascending relative record number among the records that exist. Random: the number placed in the RELATIVE KEY item

DYNAMIC is a convenience, not a faster mode. The Language Reference says it is tuned for positioning to a record and then reading a few records forward, and recommends RANDOM when records are only ever accessed by key. The statements that each open mode permits are in the OPEN section below.

The three SELECT entries below show the organizations next to each other for the Circulation book data: the line-sequential master that is read in order, an indexed copy keyed on the book id and read dynamically, and a relative copy whose slot number is the book id and that is read randomly. They are shown as entries only; the statements that use them are on the indexed and relative pages.

           SELECT BOOK-IN ASSIGN TO "BOOKMAST"
               ORGANIZATION IS LINE SEQUENTIAL
               ACCESS MODE IS SEQUENTIAL
               FILE STATUS IS WS-STATUS.
           SELECT BOOK-IDX ASSIGN TO "BOOKIDX"
               ORGANIZATION IS INDEXED
               ACCESS MODE IS DYNAMIC
               RECORD KEY IS IDX-ID
               FILE STATUS IS WS-STATUS.
           SELECT BOOK-REL ASSIGN TO "BOOKREL"
               ORGANIZATION IS RELATIVE
               ACCESS MODE IS RANDOM
               RELATIVE KEY IS WS-REL-KEY
               FILE STATUS IS WS-STATUS.

The indexed file needs a back end in GnuCOBOL: cobc --info reports the indexed file handler of the build used here as BDB (Berkeley DB), and a build without one cannot open ORGANIZATION IS INDEXED files. The Language Reference states the equivalent z/OS facts: indexed and relative files must be on direct-access storage, and the access mode must be the same in every file-control entry that refers to the same external file.

The organization also decides what an assignment name has to be on z/OS and which data-set type the file is. The Programming Guide pages that start the file chapters are the place to read how each is defined: QSAM and line-sequential files, VSAM KSDS and VSAM RRDS.

The FD entry

The file description entry, FD, is in the FILE SECTION of the DATA DIVISION. It names the file-name from the SELECT, may carry clauses about the physical file, and is followed by one or more level-01 record descriptions. The record descriptions use the same PICTURE and USAGE rules as the rest of the data division (see Data Division and Level Numbers), and the copybook BOOKREC is typically COPY-ed under the FD (see Copybooks and REPLACE).

Clause What it does Notes

RECORD CONTAINS n CHARACTERS

Format 1: the number of bytes of every record, regardless of USAGE

RECORD CONTAINS 0 is allowed for input QSAM files and takes the length from the DD statement or data set label. For line-sequential files GnuCOBOL ignores the clause with a warning, and Enterprise COBOL does not support RECORD CONTAINS 0

RECORD CONTAINS n TO m CHARACTERS

Format 2: smallest and largest record

Never required: the compiler derives the limits from the 01 records

RECORD IS VARYING IN SIZE [FROM n] [TO m] CHARACTERS [DEPENDING ON item]

Format 3: variable-length records, with the length in a separate unsigned integer item

The item must be set before every WRITE, REWRITE or RELEASE and holds the length after a successful READ

BLOCK CONTAINS [n TO] m RECORDS or CHARACTERS

The size of the physical record

BLOCK CONTAINS 0 for QSAM means "take the block size from the DD statement or the data set label", which lets the system pick a block size; it is meaningless for VSAM and cannot be combined with RECORDING MODE U

RECORDING MODE IS F, V, U or S

The format of the physical records of a QSAM file

Ignored for VSAM; when omitted, the compiler works the mode out from the record lengths

LABEL RECORDS, DATA RECORDS, VALUE OF

Document the presence of labels and the names of the records

Obsolete: syntax checked and without effect

LINAGE

Page layout of a print file

See Reports and Report Writer

EXTERNAL, GLOBAL

Share the file connector between the programs of a run unit, or with nested programs

All descriptions of an external file must agree

The Language Reference gives the four recording modes these meanings. Mode F (fixed): every record has the same length. Mode V (variable): records may differ, and each record carries a four-byte record-length field and each block a four-byte block descriptor, which are not described in the data division and not visible to the program. Mode U (undefined): one record per block, with no length fields. Mode S (spanned): a record may be larger than a block and is split into segments. Without a RECORDING MODE clause the compiler chooses F when every record has the same length (a RECORD CONTAINS n clause, or equal 01 records without OCCURS DEPENDING ON) and V when the lengths differ, when RECORD IS VARYING is used, or when RECORD CONTAINS n TO m gives different limits.

LABEL RECORDS ARE STANDARD and DATA RECORD IS name appear in a great deal of older COBOL. They are old code you may meet: Enterprise COBOL still accepts them, ignores them, and documents them as describing the labels, so remove them when they are not needed. There is no modern replacement because there is no modern need; the labels are decided by the DD statement.

Several 01 records under one FD

An FD may be followed by more than one level-01 record description. All of them describe the same storage: the record area of the file. The program does not choose among them; it uses whichever fits the record just read. This is an implicit redefinition, and it is how files with several record types (header, detail, trailer) are processed. If the records have different lengths the file is a variable-length file with the shortest and longest record derived from the descriptions, and the record area is as big as the longest.

The next program gives the loan transaction file two views of the same record: the structured LOAN-RECORD from the copybook and an alphanumeric LOAN-RAW that sees the whole 30 bytes.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FDRECORDS.
      * Two 01 records under one FD share the same storage.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT LOAN-FILE ASSIGN TO "LOANTRAN"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  LOAN-FILE.
           COPY LOANREC.
       01  LOAN-RAW                PIC X(30).
       WORKING-STORAGE SECTION.
       01  WS-STATUS               PIC XX.
       01  WS-EOF                  PIC X VALUE "N".
           88  END-OF-LOANS        VALUE "Y".
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN INPUT LOAN-FILE
           PERFORM UNTIL END-OF-LOANS
               READ LOAN-FILE
                   AT END SET END-OF-LOANS TO TRUE
                   NOT AT END
                       DISPLAY "raw: [" LOAN-RAW "]  type: " LOAN-TYPE
                           " member: " LOAN-MEMBER-ID
               END-READ
           END-PERFORM
           CLOSE LOAN-FILE
           STOP RUN.
raw: [O00010000000120261020         ]  type: O member: 000100
raw: [O00010100000220261020         ]  type: O member: 000101
raw: [R00010000000120261020         ]  type: R member: 000100
raw: [O00010200000320261020         ]  type: O member: 000102

The same record area is reached through both names, so a MOVE to LOAN-RAW changes LOAN-TYPE and vice versa. Two things follow. First, the record area belongs to the file, so after a READ it is valid only while the file is open and the record has not been replaced by the next READ; copy what the program needs with READ ... INTO or a MOVE to WORKING-STORAGE. Second, nothing in the 01 records tells the program which record type it holds; the program tests a field (here LOAN-TYPE) and then uses the matching description.

Variable-length records

A file whose records differ in length is declared with the RECORD IS VARYING format, optionally with a DEPENDING ON item. The item is an unsigned integer that carries the length between the program and the file: the program sets it before every output statement, and the run time sets it after every successful READ. The program below writes three notes of different lengths to a record-sequential file, then reads them back, using the item to display only the bytes that belong to each record.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. VARREC.
      * Variable-length records: RECORD VARYING ... DEPENDING ON.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT NOTE-FILE ASSIGN TO "NOTES.BIN"
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  NOTE-FILE
           RECORD IS VARYING IN SIZE FROM 10 TO 40 CHARACTERS
               DEPENDING ON WS-LENGTH.
       01  NOTE-RECORD             PIC X(40).
       WORKING-STORAGE SECTION.
       01  WS-STATUS               PIC XX.
       01  WS-LENGTH               PIC 9(4) BINARY.
       01  WS-EOF                  PIC X VALUE "N".
           88  END-OF-NOTES        VALUE "Y".
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN OUTPUT NOTE-FILE
           MOVE "Late return" TO NOTE-RECORD
           MOVE 11 TO WS-LENGTH
           WRITE NOTE-RECORD
           MOVE "Member moved, new address pending" TO NOTE-RECORD
           MOVE 33 TO WS-LENGTH
           WRITE NOTE-RECORD
           MOVE "Damaged copy" TO NOTE-RECORD
           MOVE 12 TO WS-LENGTH
           WRITE NOTE-RECORD
           CLOSE NOTE-FILE
           OPEN INPUT NOTE-FILE
           PERFORM UNTIL END-OF-NOTES
               READ NOTE-FILE
                   AT END SET END-OF-NOTES TO TRUE
                   NOT AT END
                       DISPLAY WS-LENGTH " [" NOTE-RECORD(1:WS-LENGTH)
                           "]"
               END-READ
           END-PERFORM
           CLOSE NOTE-FILE
           STOP RUN.
0011 [Late return]
0033 [Member moved, new address pending]
0012 [Damaged copy]

With -std=ibm the lengths were printed with five digits (00011), because the dialect makes PIC 9(4) BINARY a halfword and changes how DISPLAY formats it; the records are the same. The first 48 bytes of the file, dumped with od -A d -c, show how GnuCOBOL stores variable records: each record is preceded by a prefix of two length bytes (big-endian) and two zero bytes, and the length does not include the prefix.

0000000  \0  \v  \0  \0   L   a   t   e       r   e   t   u   r   n  \0
0000016   !  \0  \0   M   e   m   b   e   r       m   o   v   e   d   ,
0000032       n   e   w       a   d   d   r   e   s   s       p   e   n

The prefix layout is the COB_VARSEQ_FORMAT setting of the run time (value 0 is the default; other values give four-byte lengths), and the manual states that the length it records does not include the prefix. A z/OS format-V file has its own four-byte record-length field in front of each record and a four-byte block descriptor in front of each block, which the Language Reference says are not described in the data division and not available to the program. The two layouts are therefore not interchangeable, which matters when files are moved between the platforms (see Data Sets and Mainframe Utilities).

BLOCK CONTAINS and RECORDING MODE in practice

On z/OS the physical layout of a QSAM file is a property of the data set, and the program should not hard-code it. The usual FD for a fixed-length batch file therefore has RECORDING MODE IS F, BLOCK CONTAINS 0 RECORDS and the record length, and the DD statement supplies RECFM, LRECL and the block size. A typical z/OS FD for the book master therefore looks like this (z/OS; not run here). GnuCOBOL, which has no blocks, is not expected to use BLOCK CONTAINS or RECORDING MODE.

       FD  BOOK-FILE
           RECORDING MODE IS F
           BLOCK CONTAINS 0 RECORDS
           RECORD CONTAINS 60 CHARACTERS.
           COPY BOOKREC.

For a line-sequential file the RECORD CONTAINS clause has no meaning: cobc -x -Wall compiled the first program of this page with RECORD CONTAINS 60 CHARACTERS on the FD and printed warning: RECORD clause ignored for LINE SEQUENTIAL [-Wadditional], which is why the FD of that program has no clause.

On z/OS, BLOCK CONTAINS 0 lets the system choose an optimal block size when the file is created; without it the compiler assumes the file is not blocked, and even BLOCK CONTAINS 1 RECORD produces fixed blocked records. Setting BLOCK0 as a compiler option applies BLOCK CONTAINS 0 to all QSAM files that have no clause of their own.

OPEN and CLOSE

A file cannot be used until OPEN has succeeded, and every OPEN must be matched by a CLOSE before the file is opened again. The open mode says what the program will do.

Mode Allows Notes

INPUT

READ (and START for indexed and relative files)

The file must exist, unless it is OPTIONAL

OUTPUT

WRITE

Creates the file, or replaces a file that already holds records. On z/OS an existing file with records must be reusable; do not use it on a file whose contents you need

I-O

READ, REWRITE, and for indexed and relative files WRITE and DELETE

Only for direct-access files. Not valid for line-sequential files in Enterprise COBOL; GnuCOBOL 3.2 accepts it

EXTEND

WRITE that appends after the last record

For indexed and relative files, "last" means the highest key. Use SELECT OPTIONAL to create the file when it is not there

Several files can be opened in one statement, OPEN INPUT MEMBER-FILE BOOK-FILE OUTPUT REJECT-FILE, and the modes may be given in any order. After a successful OPEN the record area is available; before it, moving data to the record is a mistake. OPEN INPUT and OPEN I-O position a file at its start (for an indexed file, at the lowest key), and EXTEND positions it after the last record.

What happens when the file is not there is spelled out by the Language Reference in its availability table.

Opened as File available File not available

INPUT

Normal open

Open fails, status 35

INPUT, optional file

Normal open

Normal open, the first READ reaches the end; status 05

I-O

Normal open

Open fails, status 35

I-O, optional file

Normal open

The file is created; status 05

OUTPUT

Normal open, file is emptied

The file is created

EXTEND

Normal open

Open fails, status 35

EXTEND, optional file

Normal open

The file is created; status 05

The following program tries every case on a small file of rejected transactions, REJECTS, and prints the status after each one. The file is optional for NOTES, which does not exist. The statements that cannot work, such as writing to an input file, are deliberate.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. OPENMODES.
      * OPEN modes, optional files and the statuses they leave behind.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT REJECT-FILE ASSIGN TO "REJECTS"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
           SELECT OPTIONAL NOTE-FILE ASSIGN TO "NOTES"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  REJECT-FILE.
       01  REJECT-LINE             PIC X(30).
       FD  NOTE-FILE.
       01  NOTE-LINE               PIC X(30).
       WORKING-STORAGE SECTION.
       01  WS-STATUS               PIC XX.
       01  WS-COUNT                PIC 9(3) VALUE ZERO.
       01  WS-EOF                  PIC X VALUE "N".
           88  END-OF-FILE         VALUE "Y".
       PROCEDURE DIVISION.
       0000-MAIN-LINE.
           OPEN INPUT REJECT-FILE
           DISPLAY "INPUT, file missing        : " WS-STATUS
           OPEN INPUT NOTE-FILE
           DISPLAY "INPUT, OPTIONAL missing    : " WS-STATUS
           READ NOTE-FILE
               AT END DISPLAY "READ of optional file   : at end"
           END-READ
           CLOSE NOTE-FILE
           OPEN OUTPUT REJECT-FILE
           DISPLAY "OUTPUT creates the file    : " WS-STATUS
           MOVE "O 102 3 suspended member" TO REJECT-LINE
           WRITE REJECT-LINE
           CLOSE REJECT-FILE
           OPEN EXTEND REJECT-FILE
           DISPLAY "EXTEND                     : " WS-STATUS
           MOVE "O 101 9 unknown book" TO REJECT-LINE
           WRITE REJECT-LINE
           OPEN INPUT REJECT-FILE
           DISPLAY "OPEN of an open file       : " WS-STATUS
           CLOSE REJECT-FILE
           CLOSE REJECT-FILE
           DISPLAY "CLOSE of a closed file     : " WS-STATUS
           OPEN INPUT REJECT-FILE
           WRITE REJECT-LINE
           DISPLAY "WRITE to an INPUT file     : " WS-STATUS
           PERFORM UNTIL END-OF-FILE
               READ REJECT-FILE
                   AT END SET END-OF-FILE TO TRUE
                   NOT AT END ADD 1 TO WS-COUNT
               END-READ
           END-PERFORM
           DISPLAY "Rejects on file            : " WS-COUNT
           CLOSE REJECT-FILE
           OPEN OUTPUT REJECT-FILE
           READ REJECT-FILE
           DISPLAY "READ from an OUTPUT file   : " WS-STATUS
           CLOSE REJECT-FILE
           DELETE FILE REJECT-FILE
           DISPLAY "DELETE FILE                : " WS-STATUS
           OPEN INPUT REJECT-FILE
           DISPLAY "INPUT after DELETE FILE    : " WS-STATUS
           STOP RUN.
INPUT, file missing        : 35
INPUT, OPTIONAL missing    : 05
READ of optional file   : at end
OUTPUT creates the file    : 00
EXTEND                     : 00
OPEN of an open file       : 41
CLOSE of a closed file     : 42
WRITE to an INPUT file     : 48
Rejects on file            : 002
READ from an OUTPUT file   : 47
DELETE FILE                : 00
INPUT after DELETE FILE    : 35

The output is the same under -std=ibm. The statuses are the ones in the Language Reference: 35 for an input file that is not there, 05 for an optional file, 41 for opening a file that is already open, 42 for closing one that is not open, 47 for reading a file opened OUTPUT, and 48 for writing a file opened INPUT. The whole list is on File Status Codes.

Two statements in the program need a remark. DELETE FILE is new in COBOL 2023: it removes the external file that the file-name stands for, and GnuCOBOL 3.2 accepts it. The Enterprise COBOL 6.5 Language Reference documents only the record DELETE of indexed and relative files, so a z/OS job removes a data set in JCL or with a utility, not from COBOL. And the OPEN of an open file is an error the program can catch: the Language Reference says the EXCEPTION/ERROR procedure for the file runs.

What happens without FILE STATUS

If a file has neither a FILE STATUS item nor a USE AFTER ERROR declarative, an OPEN failure ends the program. This is a deliberately fragile program: the file NOSUCHBOOKS does not exist and nothing watches the status.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. NOSTATUS.
      * Deliberately fragile: no FILE STATUS, file does not exist.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-FILE ASSIGN TO "NOSUCHBOOKS"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-FILE.
       01  BOOK-LINE               PIC X(60).
       PROCEDURE DIVISION.
           OPEN INPUT BOOK-FILE
           DISPLAY "not reached"
           STOP RUN.
libcob: error: file does not exist (status = 35) for file BOOK-FILE ('NOSUCHBOOKS')

GnuCOBOL printed the run-time message and exit status 1; the line not reached was never displayed. On z/OS the same program fails with a Language Environment condition and an abend or a non-zero return code, depending on the options. The remedy is the one the examples above use: always code FILE STATUS, and test it after OPEN and CLOSE as well as after READ and WRITE.

CLOSE

CLOSE file-name ends the program’s use of the file. Afterwards the record area is no longer available and an OPEN is needed before any other file statement. A successful CLOSE writes any buffered records out (and, for a GnuCOBOL line-sequential file, the final line delimiter). Several files may be named in one statement. The extra phrases belong to z/OS tape and QSAM practice: REEL or UNIT and FOR REMOVAL apply to multi-volume or tape files, WITH NO REWIND to tape, and WITH LOCK prevents the file from being opened again in the same run unit. None of REEL, UNIT or NO REWIND is valid for a VSAM file, and a new program on a disk should use plain CLOSE.

The program must close what it opened, in particular before it ends. Do not rely on the run time to close files at the end of the run: an implicit close hides errors, and an output file that was not closed properly may be missing its last records. STOP RUN after an explicit CLOSE leaves no doubt.

Record locking and DELETE FILE

COBOL 2002 added record locking, and GnuCOBOL implements it: a LOCK MODE IS MANUAL or AUTOMATIC clause in the SELECT, WITH LOCK and WITH KEPT LOCK on READ, and the UNLOCK statement, with SHARING WITH to say who else may open the file. The manual says that record locking for indexed files needs a Berkeley DB build, and that a MinGW build gives up part of file sharing because the operating-system routines it needs do not exist there. The Enterprise COBOL 6.5 Language Reference has no LOCK MODE clause: sharing on z/OS is controlled by the DD statement (DISP=SHR or OLD) and, for VSAM, by the SHAREOPTIONS of the cluster, which are on Indexed Files and VSAM. A program that uses record locks is therefore not portable to z/OS.

Line-sequential files and the dialect line

LINE SEQUENTIAL is the organization of text files, and it is where the two systems differ most. In GnuCOBOL it is the everyday organization: records are lines that end with a line feed, trailing spaces are removed when a record is written (unless the run-time setting COB_LS_FIXED is on), a short line is padded with spaces when it is read, and the file is a plain text file that any editor can open. The manual also lists COB_LS_VALIDATE (check that the data in a record is valid printable text, status 09 on read and 71 on write) and COB_LS_SPLIT (what happens with a record longer than the area), whose defaults the manual gives as "per COBOL 2022". GnuCOBOL 3.2 added OPEN I-O and REWRITE for line-sequential files; in this test OPEN I-O followed by REWRITE of an unchanged record left status 00 both ways.

In Enterprise COBOL, by the Language Reference, a line-sequential file is a z/OS UNIX file whose records end with a delimiter. A record can hold only DISPLAY or DISPLAY-1 data, and a signed zoned item must carry SEPARATE CHARACTER. The clauses LABEL RECORDS, DATA RECORDS, CODE-SET, LINAGE, PADDING CHARACTER, RECORD CONTAINS 0, the second format of RECORD CONTAINS and the I-O phrase of OPEN are not supported for line-sequential files, and REWRITE is not allowed. The newline character X'15' acts as the record delimiter, so it cannot appear in the data. A batch program that exchanges files with other z/OS programs normally uses the plain SEQUENTIAL organization on QSAM data sets instead.

The practical rule for code that has to run in both places is to isolate the file definitions. Keep the FD and the record layout in a copybook, keep the SELECT in the program, and let the build or the job supply the file: DD statements on z/OS, DD_name variables in GnuCOBOL. The compilers and dialects page lists the -std=ibm switches that move GnuCOBOL’s defaults closer to Enterprise COBOL.

The same file on both systems

Question Enterprise COBOL 6.5 on z/OS GnuCOBOL 3.2

What does ASSIGN TO BOOKMAST mean?

The ddname BOOKMAST, then an environment variable BOOKMAST with DSN or PATH

A key: DD_BOOKMAST, dd_BOOKMAST, BOOKMAST in the environment, otherwise a file called BOOKMAST (with COB_FILE_PATH in front of a relative name)

Can the name contain _ or be longer than 8 characters?

No (1 to 8 letters and digits)

Yes

Where does the data set name come from?

The DD statement (DSN=) or a DSN(...) variable

The value of the variable, or the literal

Prefixes S-, AS-, VSAMDB-, label-

Defined by the Language Reference

Interpreted only under the IBM dialect; avoid them

Variable file name

Not available: change the DD or the environment variable

ASSIGN USING item

LINE SEQUENTIAL

z/OS UNIX files only, PATH(...)

Any text file; OPEN I-O and REWRITE accepted

BLOCK CONTAINS, RECORDING MODE, LABEL RECORDS

Meaningful (blocks, format) or ignored (LABEL)

Accepted, no effect

Record locking (LOCK MODE)

Not in the Language Reference

Supported with a Berkeley DB build

DELETE FILE

Not documented in 6.5

Accepted (COBOL 2023)

Default organization and access mode

SEQUENTIAL and SEQUENTIAL

SEQUENTIAL and SEQUENTIAL