This COBOL tutorial builds one small batch program from start to finish. The program opens a sequential customer file, reads fixed-width records, displays each customer, checks the file status after operations, and closes the file.
You can compile the example with GnuCOBOL on a local computer. Production COBOL environments vary, especially on IBM mainframes, so use the compiler and file-assignment conventions for the system you maintain.
What the COBOL computer language is designed to do

COBOL stands for Common Business-Oriented Language. Its syntax and data model are built for business records, decimal arithmetic, reports, and batch processing. You will often encounter it in systems where exact field layouts and predictable file operations matter more than compact syntax.
A COBOL program is organized into four divisions:
| Division | Purpose in this tutorial |
|---|---|
IDENTIFICATION DIVISION | Names the program |
ENVIRONMENT DIVISION | Maps the logical customer file to a physical file |
DATA DIVISION | Defines the file record, status fields, and end-of-file flag |
PROCEDURE DIVISION | Opens, reads, displays, and closes the file |
This structure is verbose, but it lets a maintainer locate file configuration, record layouts, and executable logic without guessing.
Set up GnuCOBOL
GnuCOBOL is an open-source COBOL compiler suitable for this local tutorial. Install it using the package instructions for your operating system, then confirm the compiler is available:
cobc --version
Create a working directory with two files:
cobol-tutorial/
├── customers.dat
└── customer-report.cbl
The sample uses free source format. Compiler defaults and available language features differ by version, so the compile command explicitly selects that format.
Create the fixed-width input file
Add these lines to customers.dat:
00001Ada Lovelace +000125050
00002Grace Hopper -000004250
00003Annie Easley +000980000
Each line is 45 characters:
- customer ID: 5 numeric characters;
- customer name: 30 characters, padded with spaces;
- balance: 10 characters containing a separate leading sign and nine digits, with two implied decimal places.
COBOL reads by position. If a field is one character too short, every field after it can be misread. Fixed-width samples are worth checking in an editor that can show column numbers.
Write the COBOL program
Add the following to customer-report.cbl:
IDENTIFICATION DIVISION.
PROGRAM-ID. CUSTOMER-REPORT.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CUSTOMER-FILE
ASSIGN TO "customers.dat"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD CUSTOMER-FILE.
01 CUSTOMER-RECORD.
05 CUSTOMER-ID PIC 9(5).
05 CUSTOMER-NAME PIC X(30).
05 CUSTOMER-BALANCE PIC S9(7)V99 SIGN IS LEADING SEPARATE.
WORKING-STORAGE SECTION.
01 WS-FILE-STATUS PIC XX VALUE SPACES.
01 WS-END-OF-FILE PIC X VALUE "N".
88 END-OF-FILE VALUE "Y".
PROCEDURE DIVISION.
MAIN.
OPEN INPUT CUSTOMER-FILE
IF WS-FILE-STATUS NOT = "00"
DISPLAY "OPEN FAILED, FILE STATUS " WS-FILE-STATUS
STOP RUN
END-IF
PERFORM UNTIL END-OF-FILE
READ CUSTOMER-FILE
AT END
SET END-OF-FILE TO TRUE
NOT AT END
DISPLAY CUSTOMER-ID " | "
CUSTOMER-NAME " | "
CUSTOMER-BALANCE
END-READ
IF WS-FILE-STATUS NOT = "00"
AND WS-FILE-STATUS NOT = "10"
DISPLAY "READ FAILED, FILE STATUS " WS-FILE-STATUS
SET END-OF-FILE TO TRUE
END-IF
END-PERFORM
CLOSE CUSTOMER-FILE
IF WS-FILE-STATUS NOT = "00"
DISPLAY "CLOSE FAILED, FILE STATUS " WS-FILE-STATUS
END-IF
STOP RUN.
The program has no placeholder sections: you can compile it as written and inspect every part discussed below.
Compile and run the example
From the working directory, run:
cobc -x -free -Wall customer-report.cbl
./customer-report
On Windows, run the generated executable using the filename produced by your environment. A successful run prints three customer records. Exact spacing and numeric display can vary with compiler and locale configuration.
The flags used here are:
-x: build an executable program;-free: parse free-format COBOL source;-Wall: enable a broad set of compiler warnings.
Treat warnings as review work. Do not remove a warning flag just to obtain a quiet build.
Understand the file connection
The SELECT clause gives the program a logical file name:
SELECT CUSTOMER-FILE
ASSIGN TO "customers.dat"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS.
ASSIGN TO maps that name to the file used by this local GnuCOBOL example. Mainframe environments commonly supply file assignments outside the program, so do not copy the local filename convention into production without checking the runtime configuration.
ORGANIZATION IS LINE SEQUENTIAL means records are separated by the platform’s line ending. Other file organizations, such as indexed and relative, support different access patterns and require different declarations.
Read the record definition
The FD entry describes one input record:
01 CUSTOMER-RECORD.
05 CUSTOMER-ID PIC 9(5).
05 CUSTOMER-NAME PIC X(30).
05 CUSTOMER-BALANCE PIC S9(7)V99 SIGN IS LEADING SEPARATE.
The PIC clauses specify the layout:
9(5)accepts five numeric positions;X(30)accepts 30 alphanumeric positions;S9(7)V99 SIGN IS LEADING SEPARATEdescribes a value with one sign character, seven integer positions, and two implied decimal positions.
The V does not consume a character in the file. It tells COBOL where the program should interpret the decimal point.
Follow the OPEN, READ, and CLOSE lifecycle
The IBM Enterprise COBOL reference for OPEN defines four common modes:
INPUTpermits reading;OUTPUTcreates or replaces output data, depending on the file and environment;I-Opermits supported read and write operations on the same file;EXTENDpositions supported files so new records can be added after existing records.
This tutorial on COBOL uses OPEN INPUT because it only reads customers.dat. IBM’s guidance also notes that a successful OPEN is required before most file input/output statements. Although this example runs under GnuCOBOL, the explicit open-process-close lifecycle is portable COBOL thinking.
The READ statement has two branches:
READ CUSTOMER-FILE
AT END
SET END-OF-FILE TO TRUE
NOT AT END
DISPLAY CUSTOMER-ID
END-READ
File status 10 commonly represents end of file for a sequential read. The program allows 00 for success and 10 for normal completion, while reporting any other status.
Why file status belongs in the example
A tutorial that shows only the happy path teaches unsafe habits. A missing input file, incompatible file attributes, or invalid operation should produce a visible result instead of silently continuing.
The two-character WS-FILE-STATUS field changes after file operations. Interpret the exact code using the documentation for your compiler and runtime. On IBM systems, for example, file availability and attribute mismatches can produce different status values depending on organization and optional-file configuration.
In production code, centralize status handling when several files share the same policy, and include the logical file name and operation in any diagnostic output.
Extend this COBOL tutorial
Once the reader program works, make one change at a time:
- Count the number of records and display the count after
CLOSE. - Add an output file and write only customers with positive balances.
- Reject a record whose numeric fields do not match the expected layout.
- Replace line-sequential input with the file organization used by the system you maintain.
- Add a test fixture with an empty file and verify the end-of-file path.
When you move from a small example to an existing application, start by documenting the program divisions, copybooks, file layouts, called programs, job-control inputs, and external data stores. A COBOL documentation generator can provide a structured starting point, but a maintainer still needs to verify runtime assignments and business rules that are not explicit in the source.
For a broader modernization path, see how to understand legacy COBOL code. This tutorial on COBOL gives you the small, runnable baseline you need before tracing a larger batch job.