COBOL COBOL 2014 (ISO/IEC 1989:2014), built with GnuCOBOL 3.x ·
✓ verified by execution on 2026-08-16
A major difference between modern scripting languages and COBOL on a mainframe is how they interact with the outside world. Modern scripts typically hardcode file paths or accept them via command-line arguments. In the mainframe world, COBOL programs are strictly executed by Job Control Language (JCL).
When you run a COBOL program on a mainframe, the JCL EXEC statement determines which compiled program to execute. The COBOL program itself knows almost nothing about the physical files on disk. This separation of logic and environment is one of COBOL’s biggest historical strengths: device independence.
The ASSIGN Clause and Device Independence
In COBOL, you never put a physical path (like C:\data\input.txt) directly in the FILE-CONTROL paragraph. Instead, you map an internal file to an external logical name using the ASSIGN TO clause.
By pointing to a DD name, the COBOL program remains device independent. You can change the physical data set (the DSN in JCL) or redirect output to a printer, all without needing to recompile the COBOL code.
What the JCL Actually Looks Like
Here is a complete, minimal job that runs the program above. This is real JCL as it would be submitted on z/OS — it cannot be executed here, because nothing outside a mainframe reads it, so read it rather than run it.
//PAYROLL JOB names the job and tells the system who to bill and where to send the printed output. Everything after it belongs to this job until the next JOB card.
//STEP010 EXEC PGM=PAYCALC is the step that actually runs something. PAYCALC is the compiled load module — the name in PROGRAM-ID, not the name of the source file. A job can have many steps, and they run in order.
//STEPLIB DD says which library holds that load module. Get this wrong and the job fails before your code runs at all.
//MYDD DD DSN=... is the line that matters most here. MYDD is the same name the COBOL wrote in ASSIGN TO MYDD. That is the whole binding: the program names a DD, the JCL names a dataset, and the system connects them at run time. Point MYDD at a different DSN tomorrow and the program processes different data without being recompiled.
//SYSOUT DD SYSOUT=* sends DISPLAY output to the job log.
Notice what is not in the COBOL: no path, no drive, no filename. That indirection is why a program written in 1985 can still be pointed at this year’s data.
Simulating JCL DD Mapping
Since we are running these examples in a GnuCOBOL environment instead of a z/OS mainframe, we don’t have JCL. However, GnuCOBOL uses environment variables as the equivalent of JCL DD statements.
By prefixing the ASSIGN name with dd_, you instruct the runtime exactly where the file lives. Below, we use COBOL’s SET ENVIRONMENT statement to dynamically assign the mapping before we open the file.
cobol✓ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. AssignTest. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT MY-FILE ASSIGN TO MYDD ORGANIZATION IS LINE SEQUENTIAL. DATA DIVISION. FILE SECTION. FD MY-FILE. 01 MY-REC PIC X(10). PROCEDURE DIVISION. SET ENVIRONMENT "dd_MYDD" TO "testout.txt" OPEN OUTPUT MY-FILE WRITE MY-REC FROM "HELLO" CLOSE MY-FILE DISPLAY "File mapped successfully." STOP RUN.
Output
File mapped successfully.
Your output
In a real production environment, you would never set the environment variable inside the COBOL program. The system operator sets it in the JCL before the program is executed.
Changing Files Without Recompiling
Because of this separation, we can map the exact same ASSIGN name to two completely different files dynamically, proving device independence in action:
cobol✓ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. DeviceInd. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT MY-FILE ASSIGN TO MYDD ORGANIZATION IS LINE SEQUENTIAL. DATA DIVISION. FILE SECTION. FD MY-FILE. 01 MY-REC PIC X(10). PROCEDURE DIVISION. SET ENVIRONMENT "dd_MYDD" TO "file1.txt" OPEN OUTPUT MY-FILE WRITE MY-REC FROM "DATA1" CLOSE MY-FILE SET ENVIRONMENT "dd_MYDD" TO "file2.txt" OPEN OUTPUT MY-FILE WRITE MY-REC FROM "DATA2" CLOSE MY-FILE DISPLAY "Wrote to two different files via DD remap." STOP RUN.
Output
Wrote to two different files via DD remap.
Your output
Missing DD Allocations
On an IBM mainframe, failing to allocate a DD statement (i.e., forgetting to include it in your JCL) will cause a hard runtime error when the program attempts to OPEN the file.
A common misconception is that different FDs (File Descriptions) are entirely isolated. But because the ASSIGN clause is what determines the physical file, two different FDs pointing to the same ASSIGN name actually share the exact same physical disk file.
Since we haven’t mapped dd_A here, GnuCOBOL will physically create a file named A. Based on that, what will this output?
Predict the outputcobol
Read the code. What exactly will it print? Commit to an answer before you look.
IDENTIFICATION DIVISION. PROGRAM-ID. P. ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. SELECT F1 ASSIGN A ORGANIZATION LINE SEQUENTIAL. SELECT F2 ASSIGN A ORGANIZATION LINE SEQUENTIAL. DATA DIVISION. FILE SECTION. FD F1. 01 R1 PIC 99. FD F2. 01 R2 PIC 99. PROCEDURE DIVISION. OPEN OUTPUT F1. WRITE R1 FROM 42. CLOSE F1. OPEN INPUT F2. READ F2. ADD 1 TO R2. DISPLAY R2. STOP RUN.
Output
43
You predicted
Because dd_A was never defined in the environment, GnuCOBOL silently mapped both logic files to a physical file named exactly A. While this prevents a crash locally, relying on unallocated DD names is considered poor practice and will immediately crash a real mainframe job!
Check yourself
How does a COBOL program connect its internal file definition to an external file?
Reveal answer
By using the ASSIGN TO clause to map to an external JCL DD name or environment variable. — The ASSIGN clause in the ENVIRONMENT DIVISION links the internal FD to an external logical name, which is then mapped to the physical file by the execution environment.
What happens on a mainframe if a required DD statement is missing when the program runs?
Reveal answer
The program throws a runtime error (like file status 35) when OPEN is attempted. — On a mainframe, if a file is not allocated via JCL (i.e., the DD name is missing), attempting to OPEN it causes a hard runtime error.
Why is the linkage between ASSIGN and DD names considered an advantage?
Reveal answer
It provides device independence, allowing you to change the physical file without recompiling the program. — Device independence means you can point the program to test data today and production data tomorrow simply by changing the JCL or environment variable, all without touching the compiled COBOL code.
Challenges
Challenge 1 +20 XP
Map the output file to 'reports.txt' by setting the correct environment variable for the OUTDD assignment before opening the file.
cobol✓ solution verified at build time
COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the
fixed code should be, then open the solution and compare. The expected output
below is real — it came from compiling the solution with GnuCOBOL when this page
was built.
Test 1 — expects "Done."
Need a hint? (−25% XP)
Use SET ENVIRONMENT "dd_OUTDD" TO "reports.txt".
Show solution (0 XP)
IDENTIFICATION DIVISION.PROGRAM-ID. ChallengeDD.ENVIRONMENT DIVISION.INPUT-OUTPUT SECTION.FILE-CONTROL. SELECT OUT-FILE ASSIGN TO OUTDD ORGANIZATION IS LINE SEQUENTIAL.DATA DIVISION.FILE SECTION.FD OUT-FILE.01 OUT-REC PIC X(20).PROCEDURE DIVISION. SET ENVIRONMENT "dd_OUTDD" TO "reports.txt" OPEN OUTPUT OUT-FILE WRITE OUT-REC FROM "REPORT GENERATED" CLOSE OUT-FILE DISPLAY "Done." STOP RUN.
🐞 Bug Hunt+30 XP
This program uses two logical files (WRITER and READER). We want them to point to the exact same physical file 'shared.dat' so we can write data and immediately read it back. Fix the bug in the environment setup.
This code runs. It just does the wrong thing. Read it, find the defect,
fix it — the tests below decide when you are right.
cobol✓ solution verified at build time
COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the
fixed code should be, then open the solution and compare. The expected output
below is real — it came from compiling the solution with GnuCOBOL when this page
was built.
Test 1 — expects "BUGFIXED"
Need a hint? (−25% XP)
The environment variable for the reader must be exactly 'dd_' plus the ASSIGN TO name.
Show solution (0 XP)
IDENTIFICATION DIVISION.PROGRAM-ID. BugHuntDD.ENVIRONMENT DIVISION.INPUT-OUTPUT SECTION.FILE-CONTROL. SELECT WRITER-FILE ASSIGN TO WRITEDD ORGANIZATION IS LINE SEQUENTIAL. SELECT READER-FILE ASSIGN TO READDD ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WS-STAT.DATA DIVISION.FILE SECTION.FD WRITER-FILE.01 W-REC PIC X(10).FD READER-FILE.01 R-REC PIC X(10).WORKING-STORAGE SECTION.01 WS-STAT PIC X(2).PROCEDURE DIVISION.*> Map both logical files to the same physical file SET ENVIRONMENT "dd_WRITEDD" TO "shared.dat" SET ENVIRONMENT "dd_READDD" TO "shared.dat" OPEN OUTPUT WRITER-FILE WRITE W-REC FROM "BUGFIXED" CLOSE WRITER-FILE OPEN INPUT READER-FILE IF WS-STAT NOT = "00" DISPLAY "ERROR: File not found!" ELSE READ READER-FILE DISPLAY R-REC CLOSE READER-FILE END-IF STOP RUN.