Cheatsheet COBOL
Linguagem empresarial clássica para sistemas mainframe e processamento batch
COBOL
Basic
Program Structure
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO-WORLD.
AUTHOR. PROGRAMMER.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NAME PIC X(30).
PROCEDURE DIVISION.
DISPLAY "Hello World".
STOP RUN.A COBOL program has 4 divisions: IDENTIFICATION (name), ENVIRONMENT (configuration), DATA (variables) and PROCEDURE (logic). Each statement ends with a period.
Format and Comments
****** Col 1-6: sequence number (optional)
****** Col 7: indicator (* = comment)
****** Col 8-11: Area A (divisions, sections)
****** Col 12-72: Area B (statements)
* Traditional comment (column 7)
*> Modern comment (inline)
DISPLAY "Code".
* Each statement ends with a period
* Keywords in UPPERCASE (convention)
* Variable names with the WS- prefixCOBOL uses fixed format: column 7 for comments, Area A (8-11) for divisions, Area B (12-72) for statements. *> is the modern inline comment.
Complete Hello World
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-MESSAGE PIC X(20)
VALUE "Hello, World!".
PROCEDURE DIVISION.
DISPLAY WS-MESSAGE.
DISPLAY "Goodbye!".
STOP RUN.
*> cobc -x hello.cob -o hello
*> ./helloThe minimal program: DISPLAY prints on screen and STOP RUN ends. Compile with cobc -x (GnuCOBOL) to create an executable.
The 4 Divisions
* 1. IDENTIFICATION - who the program is
IDENTIFICATION DIVISION.
PROGRAM-ID. MY-PROGRAM.
* 2. ENVIRONMENT - configuration and files
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
* 3. DATA - variables and structures
DATA DIVISION.
WORKING-STORAGE SECTION.
* 4. PROCEDURE - the program logic
PROCEDURE DIVISION.The 4 divisions are mandatory and in order: IDENTIFICATION, ENVIRONMENT, DATA and PROCEDURE. Only IDENTIFICATION and PROCEDURE are always required.
Compile and Run
# GnuCOBOL: cobc -x program.cob -o program ./program # Compile the a module (dynamic): cobc -m program.cob # Check syntax only: cobc -fsyntax-only program.cob # With debug: cobc -x -g -debug program.cob # Run with runtime: cobcrun program # List warnings: cobc -x -Wall program.cob
Use cobc -x for a standalone executable and cobc -m for a module. -fsyntax-only checks without compiling. cobcrun runs modules.
STOP RUN and GOBACK
PROCEDURE DIVISION.
DISPLAY "Processing...".
* End main program:
STOP RUN.
* End subprogram (returns to caller):
GOBACK.
* STOP RUN with return code:
MOVE 0 TO RETURN-CODE.
STOP RUN.STOP RUN ends a main program. GOBACK returns control to the calling program (in subprograms). RETURN-CODE indicates the status (0 = OK).
DISPLAY and ACCEPT
PROCEDURE DIVISION.
DISPLAY "Hello World".
DISPLAY "Name: " WS-NAME.
DISPLAY "Value: " WS-VAL
WITH NO ADVANCING.
ACCEPT WS-NAME.
ACCEPT WS-DATE FROM DATE.
ACCEPT WS-TIME FROM TIME.
ACCEPT WS-KEY FROM CONSOLE.DISPLAY writes on screen (can concatenate several items). ACCEPT reads from the keyboard. WITH NO ADVANCING does not move to a new line. FROM DATE/TIME reads from the system.
Paragraphs and Sections
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
PERFORM SETUP.
PERFORM PROCESS-DATA.
PERFORM CLEANUP.
STOP RUN.
SETUP.
DISPLAY "Starting...".
PROCESS-DATA.
DISPLAY "Processing...".
CLEANUP.
DISPLAY "Finishing...".Paragraphs are named blocks of code (Area A). They are called with PERFORM name. They organize the program into reusable routines.
Data
PIC Clauses
01 WS-FIELDS.
05 WS-NAME PIC X(30).
05 WS-AGE PIC 9(3).
05 WS-SALARY PIC 9(5)V99.
05 WS-CODE PIC A(5).
05 WS-FLAG PIC X.
05 WS-NEGATIVE PIC S9(4).
05 WS-EDITED PIC Z(4)9.99.
* X = alphanumeric, 9 = numeric
* A = alphabetic, V = implicit decimal
* S = signed, Z = zero suppressedThe PIC clause defines the type and size. X = text, 9 = number, V = implicit decimal place, S = sign, Z = zero suppressed (formatting).
VALUE (initial values)
01 WS-COUNTER PIC 9(4) VALUE 0.
01 WS-NAME PIC X(20) VALUE "Anonymous".
01 WS-PI PIC 9V9(6) VALUE 3.141593.
01 WS-FLAG PIC X VALUE "N".
01 WS-MAX PIC 9(3) VALUE 999.
* Constants (level 01 with VALUE):
01 WS-VAT-RATE PIC 9V99 VALUE 0.23.
* Without VALUE, fields start with spaces/zerosVALUE sets the initial value of the variable. Without VALUE, alphanumeric fields start with spaces and numeric ones with zeros. Useful for constants and defaults.
MOVE and SET
MOVE "Anna" TO WS-NAME.
MOVE 42 TO WS-AGE.
MOVE 1500.50 TO WS-SALARY.
MOVE ZERO TO WS-COUNTER.
MOVE SPACES TO WS-NAME.
MOVE ALL "X" TO WS-LINE.
MOVE HIGH-VALUE TO WS-FIELD.
SET WS-INDEX TO 1.
SET WS-INDEX UP BY 1.
SET WS-INDEX DOWN BY 1.MOVE assigns values (destination last). Special figures: ZERO, SPACES, ALL "x", HIGH-VALUE. SET is used for table indexes.
REDEFINES
01 WS-DATA.
05 WS-FULL-DATE PIC X(8).
05 WS-DATE-PARTS
REDEFINES WS-FULL-DATE.
10 WS-YEAR PIC 9(4).
10 WS-MONTH PIC 9(2).
10 WS-DAY PIC 9(2).
PROCEDURE DIVISION.
MOVE "20240315" TO WS-FULL-DATE.
DISPLAY WS-YEAR "/" WS-MONTH "/" WS-DAY.
*> Shows: 2024/03/15REDEFINES lets you view the same memory with another structure. Here an 8-char string is reinterpreted the year/month/day. It takes no extra space.
Levels and Groups
01 WS-CLIENT.
05 WS-NAME PIC X(30).
05 WS-ADDRESS.
10 WS-STREET PIC X(40).
10 WS-CITY PIC X(20).
10 WS-ZIP PIC X(8).
05 WS-PHONE PIC X(15).
* Level 01 = main record
* Level 05-49 = subfields (hierarchy)
* Level 77 = independent variable
* Level 88 = condition (boolean)Levels create hierarchy: 01 is the main group, 05-49 are nested subfields. 77 declares standalone variables. 88 defines named conditions.
Edited Fields
01 WS-VALUE PIC 9(6)V99 VALUE 1234.56.
01 WS-EDITED PIC Z(5)9.99.
01 WS-CURRENCY PIC ZZZ.ZZ9,99.
01 WS-SIGN PIC +Z(5)9.
PROCEDURE DIVISION.
MOVE WS-VALUE TO WS-EDITED.
DISPLAY WS-EDITED. *> " 1234.56"
MOVE WS-VALUE TO WS-CURRENCY.
DISPLAY WS-CURRENCY. *> " 1.234,56"Edited fields format the output: Z suppresses leading zeros, . and , insert separators, + shows the sign. Only for DISPLAY, not for calculations.
Level 88 (conditions)
01 WS-STATUS PIC X.
88 WS-ACTIVE VALUE "A".
88 WS-INACTIVE VALUE "I".
88 WS-PENDING VALUE "P".
01 WS-DAY PIC 9(2).
88 WS-WEEKEND VALUES 6, 7.
88 WS-WEEKDAY VALUES 1 THRU 5.
PROCEDURE DIVISION.
IF WS-ACTIVE
DISPLAY "Active client".
SET WS-ACTIVE TO TRUE.The level 88 creates named conditions tied to values. IF WS-ACTIVE tests whether WS-STATUS = "A". You can use multiple VALUES and THRU (range). SET ... TO TRUE activates.
Level 77 and RENAMES
* Level 77 - independent variable:
77 WS-COUNTER PIC 9(4) VALUE 0.
77 WS-FLAG PIC X VALUE "N".
* RENAMES - rename a group:
01 WS-PERSON.
05 WS-NAME PIC X(30).
05 WS-AGE PIC 9(3).
66 WS-INFO RENAMES WS-NAME
THRU WS-AGE.
* 66 groups existing fields under a new nameLevel 77 declares independent variables (no group). RENAMES (level 66) gives an alternative name to a set of existing fields.
Flow Control
IF/ELSE
IF WS-AGE >= 18
DISPLAY "Adult"
ELSE
DISPLAY "Minor"
END-IF.
IF WS-GRADE >= 14
DISPLAY "Passed"
ELSE IF WS-GRADE >= 10
DISPLAY "Resit"
ELSE
DISPLAY "Failed"
END-IF.
* Operators: = <> < > <= >=
* Logical: AND, OR, NOTIF/ELSE/END-IF is the basic conditional. Nest with ELSE IF. Operators: = (equal), <> (not equal), <, >, <=, >=. Combine with AND, OR, NOT.
EVALUATE (case)
EVALUATE WS-DAY
WHEN 1
DISPLAY "Monday"
WHEN 2 THRU 5
DISPLAY "Weekday"
WHEN 6 7
DISPLAY "Weekend"
WHEN OTHER
DISPLAY "Invalid"
END-EVALUATE.
* WHEN 6 7 = multiple values (OR)
* WHEN 2 THRU 5 = range
* WHEN OTHER = defaultEVALUATE is the "switch/case". WHEN tests each case, THRU defines ranges, values separated by a space are OR. WHEN OTHER is the default.
PERFORM VARYING (for)
* for (i = 1; i <= 10; i++):
PERFORM VARYING WS-I FROM 1 BY 1
UNTIL WS-I > 10
DISPLAY WS-I
END-PERFORM.
* Decreasing:
PERFORM VARYING WS-I FROM 10 BY -1
UNTIL WS-I < 1
DISPLAY WS-I
END-PERFORM.
* Step 2:
PERFORM VARYING WS-I FROM 0 BY 2
UNTIL WS-I > 20
DISPLAY WS-I
END-PERFORM.PERFORM VARYING is the "for" loop: FROM (start), BY (step), UNTIL (stop condition). The step can be negative or greater than 1.
EVALUATE TRUE (if/elseif)
EVALUATE TRUE
WHEN WS-AGE >= 65
DISPLAY "Senior"
WHEN WS-AGE >= 18
DISPLAY "Adult"
WHEN WS-AGE >= 12
DISPLAY "Teenager"
WHEN OTHER
DISPLAY "Child"
END-EVALUATE.
* Equivalent to an IF/ELSE-IF chain
* More readable for multiple conditionsEVALUATE TRUE evaluates boolean expressions in each WHEN. It is the clean alternative to long IF/ELSE-IF chains. The first true WHEN runs.
PERFORM UNTIL (while)
* while (answer <> "Y"):
PERFORM UNTIL WS-ANSWER = "Y"
DISPLAY "Continue? (Y/N)"
ACCEPT WS-ANSWER
END-PERFORM.
* Test at the end (do-while):
PERFORM WITH TEST AFTER
UNTIL WS-COUNTER > 5
ADD 1 TO WS-COUNTER
DISPLAY WS-COUNTER
END-PERFORM.PERFORM UNTIL is the "while" loop (test at the start). WITH TEST AFTER moves the test to the end (runs at least once, like do-while).
PERFORM paragraphs
PROCEDURE DIVISION.
PERFORM START-PROGRAM.
PERFORM PROCESS-DATA.
PERFORM END-PROGRAM.
STOP RUN.
START-PROGRAM.
DISPLAY "Starting...".
OPEN INPUT CLIENTS-FILE.
PROCESS-DATA.
PERFORM READ-RECORD
UNTIL WS-EOF = "Y".
END-PROGRAM.
CLOSE CLIENTS-FILE.PERFORM paragraph-name runs a routine and returns automatically. It is the classic way to organize COBOL into subroutines (before CALL subprograms).
PERFORM TIMES
* Repeat 5 times:
PERFORM 5 TIMES
DISPLAY "Hi"
END-PERFORM.
* With a variable:
PERFORM WS-TIMES TIMES
ADD WS-VALUE TO WS-TOTAL
END-PERFORM.
* Call a paragraph N times:
PERFORM SHOW-LINE 3 TIMES.PERFORM n TIMES repeats a block a fixed number of times. It accepts a numeric variable the the counter. The simplet form of loop.
Nested IF and Conditions
IF WS-HAS-STOCK
IF WS-PRICE <= WS-LIMIT
PERFORM BUY-ITEM
ELSE
DISPLAY "Too expensive"
END-IF
ELSE
DISPLAY "Out of stock"
END-IF.
* Compound conditions:
IF WS-AGE >= 18 AND WS-HAS-LICENSE
PERFORM RENT-CAR.
IF WS-VIP OR WS-SENIORITY > 10
PERFORM GIVE-DISCOUNT.Nest IF with clear indentation and close each one with END-IF. Combine conditions with AND/OR. Use the implicit precedence (AND before OR).
Operations
Arithmetic
ADD 10 TO WS-TOTAL.
ADD A B GIVING WS-SUM.
SUBTRACT 5 FROM WS-BALANCE.
MULTIPLY WS-PRICE BY WS-QTY
GIVING WS-TOTAL.
DIVIDE WS-TOTAL BY WS-QTY
GIVING WS-AVERAGE
REMAINDER WS-REMAINDER.
* ADD WS-A TO WS-B => WS-B += WS-A
* GIVING stores the result without altering operandsArithmetic verbs: ADD, SUBTRACT, MULTIPLY, DIVIDE. TO/FROM modify the operand; GIVING stores in a separate field. REMAINDER gives the remainder.
INSPECT and reference
* Replace characters:
INSPECT WS-TEXT
REPLACING ALL "a" BY "o".
* Count characters:
INSPECT WS-TEXT
TALLYING WS-CNT FOR ALL "e".
* Reference modification (substring):
MOVE WS-NAME(1:5) TO WS-PART.
MOVE "2024" TO WS-DATE(1:4).
* WS-NAME(1:5) = 5 chars from pos 1
* Reference with a variable:
MOVE WS-NAME(WS-POS:3) TO WS-TMP.INSPECT replaces (REPLACING) or counts (TALLYING) characters. Reference modification field(pos:len) extracts or writes substrings. Very useful for parsing.
COMPUTE (expressions)
COMPUTE WS-AREA = 3.14 * WS-R ** 2.
COMPUTE WS-INTEREST ROUNDED =
WS-CAPITAL * WS-RATE / 100.
COMPUTE WS-AVERAGE =
(WS-GRADE1 + WS-GRADE2) / 2.
COMPUTE WS-RESULT = WS-A * WS-B
+ WS-C - WS-D / WS-E.
* ** = power, ROUNDED = round
* ON SIZE ERROR handles overflow
COMPUTE WS-X = WS-Y * WS-Z
ON SIZE ERROR DISPLAY "Overflow".COMPUTE accepts full expressions with + - * / ** and parentheses. ROUNDED rounds the result. ON SIZE ERROR handles overflow.
Intrinsic Functions
* Mathematical:
COMPUTE WS-R = FUNCTION SQRT(16).
COMPUTE WS-R = FUNCTION ABS(-5).
COMPUTE WS-R = FUNCTION MAX(A B C).
COMPUTE WS-R = FUNCTION MIN(A B C).
* Strings:
MOVE FUNCTION UPPER-CASE(WS-S) TO WS-R.
MOVE FUNCTION LOWER-CASE(WS-S) TO WS-R.
MOVE FUNCTION TRIM(WS-S) TO WS-R.
MOVE FUNCTION REVERSE(WS-S) TO WS-R.
MOVE FUNCTION LENGTH(WS-S) TO WS-N.FUNCTION gives access to built-in functions: SQRT, ABS, MAX, MIN (math) and UPPER-CASE, LOWER-CASE, TRIM, REVERSE, LENGTH (strings). Always prefix with FUNCTION.
STRING (concatenate)
STRING WS-NAME DELIMITED BY SPACE
" - " DELIMITED BY SIZE
WS-CITY DELIMITED BY SPACE
INTO WS-RESULT.
* DELIMITED BY SPACE - up to the 1st space
* DELIMITED BY SIZE - whole field
* With pointer (write position):
STRING WS-A DELIMITED BY SIZE
INTO WS-B
WITH POINTER WS-POS.
* ON OVERFLOW handles lack of spaceSTRING concatenates several fields into a destination. DELIMITED BY SPACE cuts at trailing spaces, BY SIZE uses the whole field. WITH POINTER controls the position.
Comparison and Classes
* Numeric conditions:
IF WS-VALUE IS NUMERIC
IF WS-VALUE IS ZERO
IF WS-VALUE IS POSITIVE
IF WS-VALUE IS NEGATIVE
* Class conditions:
IF WS-FIELD IS ALPHABETIC
IF WS-FIELD IS ALPHANUMERIC
* Condition-name tests (level 88):
IF WS-FLAG IS TRUE
IF WS-FLAG IS FALSE
* Combined:
IF WS-A > 0 AND WS-B < 100
IF NOT WS-ERRORClass tests: IS NUMERIC, IS ALPHABETIC, IS ALPHANUMERIC. Value tests: IS ZERO, IS POSITIVE, IS NEGATIVE. Level 88 condition tests: IS TRUE/FALSE.
UNSTRING (split)
* Split by delimiter:
UNSTRING WS-DATE DELIMITED BY "/"
INTO WS-DAY WS-MONTH WS-YEAR.
* Multiple delimiters:
UNSTRING WS-LINE DELIMITED BY ","
OR ";" INTO WS-C1 WS-C2 WS-C3.
* With counters:
UNSTRING WS-PHRASE DELIMITED BY " "
INTO WS-W1 WS-W2 WS-W3
TALLYING IN WS-NUM-WORDS.
* WS-DATE = "15/03/2024"
* => WS-DAY=15, WS-MONTH=03, WS-YEAR=2024UNSTRING splits a string by delimiter into several fields. It accepts multiple delimiters with OR. TALLYING IN counts how many segments were extracted.
files
Define Files
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT CLIENTS
ASSIGN TO "clients.dat"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-STATUS.
DATA DIVISION.
FILE SECTION.
FD CLIENTS.
01 REC-CLIENT.
05 REC-NAME PIC X(30).
05 REC-AGE PIC 9(3).
05 REC-BALANCE PIC 9(6)V99.Declare it in FILE-CONTROL (SELECT/ASSIGN) and define the record in FD (FILE SECTION). FILE STATUS stores the result code of each operation.
Indexed File
SELECT CLIENTS
ASSIGN TO "clients.idx"
ORGANIZATION IS INDEXED
ACCESS MODE IS DYNAMIC
RECORD KEY IS REC-CODE
ALTERNATE KEY IS REC-NAME
WITH DUPLICATES.
* Read by key:
MOVE 123 TO REC-CODE.
READ CLIENTS
KEY IS REC-CODE
INVALID KEY DISPLAY "Not found"
END-READ.ORGANIZATION INDEXED allows direct access by key. RECORD KEY is the primary key, ALTERNATE KEY are secondary keys. ACCESS MODE DYNAMIC allows sequential + direct.
Sequential Read
PROCEDURE DIVISION.
OPEN INPUT CLIENTS.
IF WS-STATUS NOT = "00"
DISPLAY "Error: " WS-STATUS
STOP RUN
END-IF.
PERFORM UNTIL WS-EOF = "Y"
READ CLIENTS
AT END
MOVE "Y" TO WS-EOF
NOT AT END
DISPLAY REC-NAME
END-READ
END-PERFORM.
CLOSE CLIENTS.OPEN INPUT opens for reading. The READ ... AT END loop processes record by record until the end. CLOSE releases the file. Classic batch processing pattern.
START and Sequential Read
* Position at the start (key >= 100):
MOVE 100 TO REC-CODE.
START CLIENTS
KEY IS >= REC-CODE
INVALID KEY DISPLAY "No records".
* Read sequentially from there:
PERFORM UNTIL WS-EOF = "Y"
READ CLIENTS NEXT
AT END MOVE "Y" TO WS-EOF
NOT AT END DISPLAY REC-CODE
END-READ
END-PERFORM.START positions the indexed file at a key (with =, >, >=, <). Then READ NEXT goes sequentially from there. Ideal for range reports.
Write
* Create/overwrite:
OPEN OUTPUT CLIENTS.
MOVE "Anna" TO REC-NAME.
MOVE 30 TO REC-AGE.
WRITE REC-CLIENT.
CLOSE CLIENTS.
* Append to the end:
OPEN EXTEND CLIENTS.
WRITE REC-CLIENT.
CLOSE CLIENTS.
* WRITE with check:
WRITE REC-CLIENT
INVALID KEY DISPLAY "Write error".OPEN OUTPUT creates/overwrites. OPEN EXTEND appends to the end. WRITE writes the FD record. INVALID KEY detects write errors.
DELETE
OPEN I-O CLIENTS.
* Position at the record:
MOVE 123 TO REC-CODE.
READ CLIENTS
KEY IS REC-CODE
INVALID KEY DISPLAY "Does not exist"
END-READ.
* Delete:
DELETE CLIENTS RECORD
INVALID KEY DISPLAY "Delete error".
CLOSE CLIENTS.
* Only works on indexed/relative filesDELETE file RECORD removes the current record. Requires the file open in I-O and a previously read record. Only works on indexed or relative organizations.
Update (I-O)
OPEN I-O CLIENTS.
* Read the record to modify:
MOVE 123 TO REC-CODE.
READ CLIENTS
KEY IS REC-CODE
INVALID KEY DISPLAY "Does not exist"
END-READ.
* Modify and rewrite:
MOVE 999.99 TO REC-BALANCE.
REWRITE REC-CLIENT
INVALID KEY DISPLAY "Error".
CLOSE CLIENTS.OPEN I-O opens for reading and writing. Read the record, modify the fields and use REWRITE to save the changes in place. Requires an indexed or relative file.
FILE STATUS
01 WS-STATUS PIC XX.
* Common codes:
* "00" - Success
* "02" - Success (duplicate on alternate key)
* "10" - End of file
* "23" - Record not found
* "35" - File does not exist
* "46" - Invalid read (sequential)
* "9x" - System error
IF WS-STATUS = "00"
DISPLAY "OK"
ELSE
DISPLAY "Error: " WS-STATUS
END-IF.The FILE STATUS (2 chars) stores the result of each file operation. "00" = success, "10" = EOF, "23" = not found, "35" = nonexistent file. Always check after OPEN/READ/WRITE.
Advanced
COPY and COPYBOOK
* COPYBOOK file (common.cpy):
01 WS-COMMON-DATA.
05 WS-USER PIC X(20).
05 WS-SYS-DATE PIC 9(8).
05 WS-SYS-TIME PIC 9(6).
* In the program:
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY "common.cpy".
* With REPLACING (rename prefix):
COPY "common.cpy"
REPLACING WS- BY CLI-.COPY inserts a copybook (shared code). REPLACING renames prefixes to avoid conflicts. Essential for sharing structures between programs.
SORT and MERGE
* Sort a file:
SORT WORK-FILE
ON ASCENDING KEY REC-CODE
USING INPUT-FILE
GIVING OUTPUT-FILE.
* With INPUT/OUTPUT procedures:
SORT WORK-FILE
ON DESCENDING KEY REC-BALANCE
INPUT PROCEDURE IS READ-DATA
OUTPUT PROCEDURE IS WRITE-DATA.
* MERGE joins sorted files:
MERGE WORK-FILE
ON ASCENDING KEY REC-CODE
USING FILE-A FILE-B
GIVING FILE-C.SORT sorts a file by key (ASCENDING/DESCENDING). MERGE joins already sorted files. USING/GIVING define input and output. Native COBOL sorting.
Subprograms (CALL)
* Calling program:
CALL "SUBPROG" USING
WS-PARAM1
WS-PARAM2
WS-RESULT.
* Subprogram (SUBPROG.COB):
IDENTIFICATION DIVISION.
PROGRAM-ID. SUBPROG.
DATA DIVISION.
LINKAGE SECTION.
01 LS-PARAM1 PIC X(10).
01 LS-PARAM2 PIC 9(5).
01 LS-RESULT PIC 9(8).
PROCEDURE DIVISION USING
LS-PARAM1 LS-PARAM2 LS-RESULT.
COMPUTE LS-RESULT = LS-PARAM2 * 2.
GOBACK.CALL "name" USING calls a subprogram passing parameters. The subprogram declares them in the LINKAGE SECTION and receives them in PROCEDURE DIVISION USING. It ends with GOBACK.
Error Handling
* FILE STATUS:
IF WS-STATUS = "00"
DISPLAY "Success"
ELSE IF WS-STATUS = "10"
DISPLAY "End of file"
ELSE IF WS-STATUS = "23"
DISPLAY "Record does not exist"
ELSE
DISPLAY "Error: " WS-STATUS
END-IF.
* USE declaratives (global handler):
DECLARATIVES.
ERROR-HANDLING SECTION.
USE AFTER STANDARD ERROR
PROCEDURE ON CLIENTS.
DISPLAY "File error".
END DECLARATIVES.Check the FILE STATUS after each operation. DECLARATIVES defines global error handlers that run automatically when a file error occurs.
BY VALUE/REFERENCE Parameters
* By reference (default - can modify):
CALL "SUB" USING WS-DATUM.
* By value (copy - does not modify):
CALL "SUB" USING BY VALUE WS-NUM.
* In the subprogram (BY VALUE):
PROCEDURE DIVISION USING
BY VALUE LS-NUM.
* BY CONTENT (read-only copy):
CALL "SUB" USING BY CONTENT WS-TEXT.
* Return a value:
CALL "SUB" USING WS-X
RETURNING WS-RESULT.By default parameters are BY REFERENCE (the subprogram can modify them). BY VALUE passes a copy. RETURNING returns a value like a function.
Pointers and ADDRESS
01 WS-POINTER USAGE POINTER.
01 WS-DATA PIC X(100).
* Get the address:
SET WS-POINTER TO ADDRESS OF WS-DATA.
* Allocate dynamic memory:
ALLOCATE 100 CHARACTERS
RETURNING WS-POINTER.
* Free:
FREE WS-POINTER.
* SET POINTER to a position:
SET WS-POINTER UP BY 10.
* Compare pointers:
IF WS-POINTER = NULL
DISPLAY "Out of memory".USAGE POINTER declares pointers. ADDRESS OF gets the address of a field. ALLOCATE/FREE manage dynamic memory. Useful for variable-length structures.
INITIALIZE
01 WS-RECORD.
05 WS-NAME PIC X(20).
05 WS-AGE PIC 9(3).
05 WS-BALANCE PIC 9(6)V99.
* Clear everything (spaces + zeros):
INITIALIZE WS-RECORD.
* Only alphanumeric fields:
INITIALIZE WS-RECORD
REPLACING ALPHANUMERIC BY SPACES.
* With a specific value:
INITIALIZE WS-RECORD
REPLACING NUMERIC DATA BY ZERO.
* Without INITIALIZE, fields keep garbageINITIALIZE clears a group: alphanumerics to spaces, numerics to zeros. REPLACING lets you specify the values. Avoids memory "garbage" in reused records.
Tables
OCCURS (arrays)
01 WS-TABLE.
05 WS-ITEM OCCURS 10 TIMES
INDEXED BY WS-IDX.
10 WS-CODE PIC 9(4).
10 WS-NAME PIC X(20).
* Traverse:
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > 10
DISPLAY WS-NAME(WS-IDX)
END-PERFORM.
* Access by index:
MOVE 5 TO WS-IDX.
DISPLAY WS-CODE(WS-IDX).OCCURS n TIMES creates an array with n elements. INDEXED BY declares an index. Access with field(index). Indexes start at 1.
SET UP / DOWN
01 WS-TAB.
05 WS-ELEM OCCURS 10 TIMES
INDEXED BY WS-IDX
PIC X(10).
* Increment/decrement the index:
SET WS-IDX TO 1.
SET WS-IDX UP BY 1. *> WS-IDX = 2
SET WS-IDX DOWN BY 1. *> WS-IDX = 1
* Traverse backwards:
SET WS-IDX TO 10.
PERFORM VARYING WS-IDX FROM 10 BY -1
UNTIL WS-IDX < 1
DISPLAY WS-ELEM(WS-IDX)
END-PERFORM.SET index UP BY n increments, DOWN BY n decrements. Indexes (INDEXED BY) can only be modified with SET, not with MOVE/COMPUTE.
SEARCH (linear search)
SET WS-IDX TO 1.
SEARCH WS-ITEM
AT END
DISPLAY "Not found"
WHEN WS-CODE(WS-IDX) = WS-TARGET
DISPLAY "Found: " WS-NAME(WS-IDX)
END-SEARCH.
* SEARCH scans from the current index
* Stops at the first true WHEN
* AT END runs if there is no matchSEARCH does a linear search on the table. Set the index first (SET ... TO 1). WHEN tests each element, AT END runs if not found.
OCCURS DEPENDING ON
01 WS-TABLE.
05 WS-COUNT PIC 9(3).
05 WS-ITEM OCCURS 1 TO 100 TIMES
DEPENDING ON WS-COUNT
PIC X(20).
* Define how many elements to use:
MOVE 50 TO WS-COUNT.
* Now WS-ITEM has 50 valid elements
PERFORM VARYING WS-I FROM 1 BY 1
UNTIL WS-I > WS-COUNT
DISPLAY WS-ITEM(WS-I)
END-PERFORM.OCCURS DEPENDING ON creates variable-length tables. The counter field (WS-COUNT) defines how many elements are active. Saves memory at runtime.
SEARCH ALL (binary)
* The table MUST be sorted by the key:
SEARCH ALL WS-ITEM
AT END
DISPLAY "Not found"
WHEN WS-CODE(WS-IDX) = WS-TARGET
DISPLAY "Found: " WS-NAME(WS-IDX)
END-SEARCH.
* SEARCH ALL = binary search (O(log n))
* Faster than SEARCH for large tables
* Requires ASCENDING/DESCENDING KEY on OCCURSSEARCH ALL uses binary search (much faster). The table must be sorted. Declare ASCENDING KEY on the OCCURS. Ideal for large tables.
Fill a Table
PERFORM VARYING WS-I FROM 1 BY 1
UNTIL WS-I > 10
DISPLAY "Value " WS-I ":"
ACCEPT WS-VALUE(WS-I)
END-PERFORM.
* Or read from a file:
MOVE 0 TO WS-COUNT.
PERFORM UNTIL WS-EOF = "Y" OR
WS-COUNT >= 100
READ DATA-FILE AT END MOVE "Y" TO WS-EOF
NOT AT END
ADD 1 TO WS-COUNT
MOVE REC-FIELD TO
WS-ITEM(WS-COUNT)
END-READ
END-PERFORM.Fill tables with PERFORM VARYING + ACCEPT or by reading from a file record by record. Always control the maximum limit so you do not exceed the OCCURS.
Multidimensional Table
01 WS-MATRIX.
05 WS-ROW OCCURS 3 TIMES.
10 WS-COLUMN OCCURS 4 TIMES
PIC 9(4).
* Access [row][column]:
MOVE 100 TO WS-MATRIX(2, 3).
DISPLAY WS-MATRIX(1, 1).
* Traverse everything:
PERFORM VARYING WS-R FROM 1 BY 1
UNTIL WS-R > 3
PERFORM VARYING WS-C FROM 1 BY 1
UNTIL WS-C > 4
DISPLAY WS-MATRIX(WS-R, WS-C)
END-PERFORM
END-PERFORM.Nest OCCURS to create matrices. Access with (row, column). Traverse with nested PERFORM VARYING (one per dimension).
Tips and Good Practices
Naming Conventions
* Prefixes by type:
01 WS-COUNTER PIC 9(4). *> Working Storage
01 REC-CLIENT PIC X(30). *> FD record
01 LS-PARAM PIC 9(5). *> Linkage Section
01 FD-DATA PIC X(10). *> File Description
* Descriptive names with hyphens:
01 WS-TOTAL-SALES PIC 9(8)V99.
01 WS-CLIENT-NAME PIC X(40).
* Level 88 with condition names:
88 WS-VALID-RECORD VALUE "Y".
* Maximum 30 characters per nameUse prefixes: WS- (working storage), REC- (records), LS- (linkage). Descriptive names with hyphens. Level 88 with names that read like conditions.
Modern COBOL (2002+)
* Inline comments:
*> This is a modern comment
* Real booleans:
01 WS-OK PIC 1 VALUE 0.
SET WS-OK TO TRUE.
* Intrinsic functions:
COMPUTE WS-R = FUNCTION SQRT(16).
* Reference modification:
MOVE WS-S(1:3) TO WS-T.
* SELF (stateful programs):
IDENTIFICATION DIVISION.
PROGRAM-ID. COUNTER IS RECURSIVE.
* GnuCOBOL supports most standards
* COBOL 85, 2002, 2014 and 2023Modern COBOL (2002+) added *> comments, booleans, intrinsic functions and reference modification. GnuCOBOL supports the 85, 2002, 2014 and 2023 standards.
Clean Program Structure
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-INITIALIZE.
PERFORM 2000-PROCESS
UNTIL WS-EOF = "Y".
PERFORM 9000-FINALIZE.
STOP RUN.
1000-INITIALIZE.
OPEN INPUT CLIENTS.
MOVE 0 TO WS-COUNTER.
2000-PROCESS.
READ CLIENTS
AT END MOVE "Y" TO WS-EOF
END-READ.
9000-FINALIZE.
CLOSE CLIENTS.
DISPLAY "Total: " WS-COUNTER.Number the paragraphs (1000-, 2000-, 9000-) for organization and easy insertion. MAIN calls INITIALIZE, PROCESS and FINALIZE. Classic maintenance pattern.
GnuCOBOL
# Install: # Ubuntu: sudo apt install gnucobol # Windows: via MSYS2 or binaries # Compile and run: cobc -x program.cob -o program ./program # Tools: cobc --version *> version cobc --list-intrinsics *> available functions cobcrun --runtime-conf *> configuration # Free format (no fixed columns): cobc -x -free program.cbl # Optimization: cobc -x -O2 program.cob
GnuCOBOL is the most widely used open-source compiler. -free allows free format (no fixed columns). -O2 optimizes. --list-intrinsics shows the available functions.
Debug with DISPLAY
* Show values during execution:
DISPLAY "WS-COUNTER = " WS-COUNTER.
DISPLAY "STATUS = " WS-STATUS.
DISPLAY "RECORD: " REC-CLIENT.
* With UPON to separate:
DISPLAY "DEBUG" UPON SYSERR.
* GnuCOBOL - execution trace:
*> cobc -x -debug -ftraceall program.cob
*> COB_SET_TRACE=1 ./program
* ACCEPT to pause:
DISPLAY "Press ENTER to continue".
ACCEPT WS-KEY.DISPLAY is the main debugging tool. Show variables at key points. GnuCOBOL supports -ftraceall for a full trace. ACCEPT pauses execution.
Program Template
IDENTIFICATION DIVISION.
PROGRAM-ID. TEMPLATE.
AUTHOR. YOUR-NAME.
DATE-WRITTEN. 2024-01-15.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 WS-COUNTER PIC 9(4) VALUE 0.
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-START.
PERFORM 2000-PROCESS.
PERFORM 9000-END.
STOP RUN.
1000-START.
2000-PROCESS.
9000-END.A standard template with a header (AUTHOR, DATE-WRITTEN), empty divisions and numbered paragraphs. Copy and adapt it for new programs — it saves time and keeps consistency.
Common Mistakes
* 1. Forgetting the period:
DISPLAY "Hi" *> ERROR!
DISPLAY "Hi". *> correct
* 2. MOVE in the wrong order:
MOVE WS-A TO WS-B. *> WS-B receives WS-A
* 3. PIC 9 with a decimal value without V:
01 WS-X PIC 99.
MOVE 3.14 TO WS-X. *> becomes 31 (truncated!)
01 WS-Y PIC 9V99. *> correct: 3.14
* 4. Comparing alphanumeric with numeric:
IF WS-NAME = 100 *> type ERROR
IF WS-NAME = "100" *> correct
* 5. Index outside the OCCURSFrequent mistakes: missing period, MOVE order reversed, PIC without V for decimals (truncates!), comparing different types, index outside the table.