MainframeJCL

JCL Tutorial for Beginners: Complete Guide to Job Control Language (2026)

TT
TopicTrick Team
JCL Tutorial for Beginners: Complete Guide to Job Control Language (2026)

JCL Tutorial: Complete Guide to Job Control Language

Job Control Language (JCL) is the scripting language used to submit and manage batch jobs on IBM mainframe systems running z/OS. Despite being over 50 years old, JCL remains essential in finance, insurance, healthcare, and government — industries where mainframes process hundreds of billions of transactions every day.

This tutorial covers everything you need to write real JCL from scratch: the three core statement types, data set handling, procedures, utilities, and error handling.


What is JCL?

JCL is the interface between you and z/OS. When you want to run a COBOL program, sort a file, or copy a data set on a mainframe, you don't type commands into a shell — you submit a JCL job. The z/OS Job Entry Subsystem (JES2 or JES3) receives the job, schedules it according to priority and resource availability, and runs it.

A JCL job is made up of statements that answer three questions:

  1. Who is submitting this job and how should it be run? (JOB statement)
  2. Which program should be executed? (EXEC statement)
  3. What data does that program need? (DD statements)

JCL Statement Format

Every JCL statement follows a rigid positional format. Columns matter.

text
//name     operation  operands           comments
//STEP1    EXEC       PGM=IEBGENER
ColumnContent
1–2// (identifies a JCL statement)
3–10Name field (optional; must start in column 3 if present)
12+Operation keyword (JOB, EXEC, DD, etc.)
After operationOperands, separated by commas
After operandsComments (separated by at least one space)

A statement beginning with //* is a comment. A statement beginning with /* is a delimiter statement.


The JOB Statement

The JOB statement is always the first statement in a job. It identifies the job to z/OS and specifies execution parameters.

jcl
//MYJOB    JOB  (ACCT001),'MY JOB NAME',
//             CLASS=A,
//             MSGCLASS=X,
//             MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             REGION=0M

Key JOB Parameters

ParameterDescription
(accounting-info)First positional — accounting/billing information for the installation
'programmer-name'Second positional — free-text identifier (up to 20 characters)
CLASS=Job class — controls which initiator (execution environment) picks up the job
MSGCLASS=Sysout class — where job output (JES log, SYSPRINT) is directed
MSGLEVEL=(stmts,msgs)Controls how much JCL and system messages appear in the job log
NOTIFY=&SYSUIDSends a TSO message to the submitter when the job ends
REGION=0MMaximum virtual memory — 0M means use the system maximum
TIME=(mins,secs)CPU time limit before the job is cancelled

The EXEC Statement

The EXEC statement defines a job step — one unit of work. A job can contain multiple steps; each step runs a program or procedure.

EXEC PGM= (Running a Program Directly)

jcl
//STEP1    EXEC PGM=IEBGENER

PGM= specifies the load module name exactly as it appears in the system's program library (LINKLIST or STEPLIB).

EXEC PROC= (Invoking a Procedure)

jcl
//STEP1    EXEC PROC=MYPROC
//STEP1    EXEC MYPROC          (shorthand — PROC= is assumed)

Key EXEC Parameters

ParameterDescription
PGM=nameRun this load module
PROC=nameExecute this catalogued procedure
COND=Condition code test — skip or run this step depending on prior return codes
PARM=Pass a string of up to 100 characters to the program at runtime
REGION=Override the JOB-level region size for this step only
TIME=CPU time limit for this step

Understanding COND=

jcl
//STEP2    EXEC PGM=MYPROG,COND=(4,LT,STEP1)

COND=(4,LT,STEP1) means: skip this step if 4 is less than the return code of STEP1 — i.e., run STEP2 only if STEP1 completed with return code ≥ 4. The condition is tested from the perspective of the comparison operand.

Common interpretations:

  • COND=(0,NE) — skip if any prior step returned non-zero (skip on any error)
  • COND=(4,LT) — skip if any prior step returned a code > 4

The DD Statement

DD (Data Definition) statements describe every data set a program reads or writes. Each DD in a step has a name that matches a //ddname reference inside the program's code.

jcl
//INPUT1   DD   DSN=MY.INPUT.FILE,
//              DISP=(OLD,KEEP,KEEP),
//              UNIT=SYSDA,
//              SPACE=(TRK,(10,5))

DSN — Data Set Name

DSN= (or DSNAME=) specifies the fully-qualified name of the data set. On z/OS, data set names are hierarchical, separated by dots, up to 44 characters.

jcl
DSN=PROD.PAYROLL.MASTER
DSN=&&TEMPFILE          (temporary data set — double ampersand)
DSN=*.STEP1.MYDATA      (backward reference — use data set from STEP1)

DISP — Disposition

DISP is one of the most important DD parameters. It has up to three sub-parameters:

text
DISP=(status, normal-termination, abnormal-termination)

Status values:

ValueMeaning
NEWCreate a new data set
OLDData set exists; request exclusive access
SHRData set exists; allow shared read access
MODExtend an existing sequential data set (or create if not found)

Disposition values (normal and abnormal):

ValueMeaning
KEEPRetain the data set after the step
DELETEDelete the data set after the step
CATLGCatalogue the data set (register it with the system catalog)
UNCATLGRemove from catalog but keep on disk
PASSPass to the next step in the same job

Common DISP combinations:

jcl
DISP=SHR                       (existing catalogued data set, shared access)
DISP=(NEW,CATLG,DELETE)        (create, keep on success, delete on failure)
DISP=(OLD,DELETE)              (exclusive access, delete after step)
DISP=(NEW,PASS,DELETE)         (temporary — pass to next step)
DISP=(MOD,CATLG,CATLG)         (append to existing sequential data set)

UNIT and SPACE

jcl
UNIT=SYSDA                     (generic disk unit)
SPACE=(CYL,(5,2),RLSE)         (5 cylinders primary, 2 secondary, release unused)
SPACE=(TRK,(100,50))           (100 tracks primary, 50 secondary)
SPACE=(80,(1000,500),RLSE)     (block size 80, 1000 blocks primary)

DCB — Data Control Block

DCB describes the physical characteristics of the data set:

jcl
DCB=(RECFM=FB,LRECL=80,BLKSIZE=8000)
Sub-parameterDescription
RECFM=FBFixed Blocked records
RECFM=VBVariable Blocked records
RECFM=UUndefined record format
LRECL=80Logical record length in bytes
BLKSIZE=8000Physical block size

Special DD Names

z/OS and most programs reserve certain DD names:

DD NamePurpose
JOBLIBLoad library search — programs found here before LINKLIST
STEPLIBStep-level load library — overrides JOBLIB for one step
SYSPRINTStandard program output (messages, reports)
SYSINStandard program input (control cards, SQL, parameters)
SYSOUTSysout data set — output routed to a print class
SYSUDUMPDump data set — receives storage dumps on abend
SYSABENDSimilar to SYSUDUMP — formatted dump

Sysout DD — Writing to Print

To direct output to the JES spool (for printing or viewing):

jcl
//SYSPRINT DD  SYSOUT=*          (same class as MSGCLASS)
//REPORT   DD  SYSOUT=A          (class A — usually the default print class)
//HOLD     DD  SYSOUT=(A,INTRDR) (send to internal reader — submit as a job)

In-Stream Data Sets

You can embed data directly inside JCL using DD * or DD DATA:

jcl
//SYSIN    DD  *
  SORT FIELDS=(1,10,CH,A)
  RECORD TYPE=F,LENGTH=80
/*

DD * terminates at /* or at the next JCL statement. Use DD DATA,DLM=@@ if your in-stream data itself contains /*.


JCL Procedures

A procedure (PROC) is a reusable block of JCL steps. You define it once and invoke it from multiple jobs.

Catalogued Procedure

Stored in a system procedure library (SYS1.PROCLIB or a site-specific library):

jcl
//COBRUN   PROC
//COMPILE  EXEC PGM=IGYCRCTL,PARM='LIST,MAP'
//SYSPRINT DD  SYSOUT=*
//SYSLIN   DD  DSN=&&LOADSET,DISP=(NEW,PASS),
//             UNIT=SYSDA,SPACE=(TRK,(10,5))
//SYSIN    DD  DDNAME=COBOL
          PEND

Invoke it:

jcl
//STEP1   EXEC COBRUN
//COMPILE.COBOL DD DSN=MY.COBOL.SOURCE,DISP=SHR

Note: COMPILE.COBOL overrides the COBOL DD inside the COMPILE step of the COBRUN procedure.

In-Stream Procedure

Defined within the job itself, between PROC and PEND:

jcl
//MYJOB    JOB ...
//MYPROC   PROC
//STEP1    EXEC PGM=MYPROG
//SYSPRINT DD  SYSOUT=*
//         PEND
//
//CALLSTEP EXEC MYPROC

Symbolic Parameters

Procedures can accept parameters using &symbol notation:

jcl
//COPYPROC PROC INDSN=,OUTDSN=
//COPY     EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=&INDSN,DISP=SHR
//SYSUT2   DD  DSN=&OUTDSN,DISP=(NEW,CATLG,DELETE),
//             UNIT=SYSDA,SPACE=(TRK,(10,5))
//SYSIN    DD  DUMMY
//SYSPRINT DD  SYSOUT=*
          PEND

//STEP1    EXEC COPYPROC,INDSN=PROD.INPUT,OUTDSN=PROD.OUTPUT

Generation Data Groups (GDGs)

A GDG is a series of related sequential data sets managed as a group. They're used for rolling backups, audit trails, and periodic processing (daily, weekly batch).

jcl
//OUTPUT   DD  DSN=MY.GDG(+1),          (create new generation)
//             DISP=(NEW,CATLG,DELETE),
//             UNIT=SYSDA,
//             SPACE=(TRK,(10,5)),
//             DCB=MY.GDG.MODEL

//INPUT    DD  DSN=MY.GDG(0),DISP=SHR   (current/latest generation)
//PREV     DD  DSN=MY.GDG(-1),DISP=SHR  (previous generation)

The GDG base must be created first (usually with IDCAMS DEFINE GDG).


Common JCL Utilities

z/OS ships with a set of utility programs for routine data management tasks:

IEBGENER — Copy Sequential Data Sets

jcl
//COPY     EXEC PGM=IEBGENER
//SYSUT1   DD  DSN=SOURCE.FILE,DISP=SHR
//SYSUT2   DD  DSN=TARGET.FILE,DISP=(NEW,CATLG,DELETE),
//             UNIT=SYSDA,SPACE=(TRK,(10,5))
//SYSIN    DD  DUMMY
//SYSPRINT DD  SYSOUT=*

IEFBR14 — Do Nothing (Allocate/Delete Data Sets)

The "do-nothing" program — used purely to allocate or delete data sets via DD statements:

jcl
//DELSTEP  EXEC PGM=IEFBR14
//DELFILE  DD  DSN=OLD.FILE.TO.DELETE,
//             DISP=(OLD,DELETE,DELETE)

SORT (DFSORT or SyncSort) — Sort and Merge

jcl
//SORTJOB  EXEC PGM=SORT
//SORTIN   DD  DSN=INPUT.FILE,DISP=SHR
//SORTOUT  DD  DSN=SORTED.FILE,DISP=(NEW,CATLG,DELETE),
//             UNIT=SYSDA,SPACE=(CYL,(5,2))
//SYSIN    DD  *
  SORT FIELDS=(1,10,CH,A,11,5,ZD,D)
  RECORD TYPE=F,LENGTH=80
/*
//SYSOUT   DD  SYSOUT=*

The SORT control statement above: sort ascending by bytes 1–10 (character), then descending by bytes 11–15 (zoned decimal).

IDCAMS — VSAM and Catalog Management

jcl
//VSAM     EXEC PGM=IDCAMS
//SYSPRINT DD  SYSOUT=*
//SYSIN    DD  *
  DEFINE CLUSTER -
    (NAME(MY.VSAM.KSDS) -
     INDEXED -
     KEYS(10 0) -
     RECORDSIZE(200 200) -
     TRACKS(50 10) -
     VOLUMES(WORK01)) -
  DATA -
    (NAME(MY.VSAM.KSDS.DATA)) -
  INDEX -
    (NAME(MY.VSAM.KSDS.INDEX))
/*
//SYSOUT   DD  SYSOUT=*

JCL Condition Codes and Return Codes

Every program that runs under z/OS returns a condition code (return code) between 0 and 4095. The conventions are:

Return CodeConventional Meaning
0Successful — no errors
4Warning — processing completed with minor issues
8Error — results may be incomplete or unreliable
12Severe error — processing stopped early
16Terminal error — do not use the output

Use COND= on later EXEC statements to skip steps based on prior return codes, ensuring a job doesn't run downstream steps against corrupted output.


JCL Abend Codes

An abend (abnormal end) occurs when a program crashes or is cancelled. Abend codes appear in the JES job log:

Abend TypeExampleMeaning
System abendS0C7Data exception — invalid data for operation (often bad numeric data)
System abendS0C4Protection exception — program accessed storage it doesn't own
System abendS322CPU time limit exceeded
System abendS806Load module not found
System abendS913Authorisation failure (security)
User abendU1000User-defined abend in the application program

JCL Restart and Checkpointing

To restart a failed job from a specific step (instead of re-running from the beginning):

jcl
//MYJOB    JOB  ...,RESTART=STEP3

Or restart with step overrides:

jcl
//MYJOB    JOB  ...,RESTART=(STEP3.PROC1)

For long-running jobs, COBOL programs can use CHKPT with DFSMS checkpointing to restart within a step.


VSAM Data Sets in JCL

VSAM (Virtual Storage Access Method) data sets are accessed differently from sequential files. They're catalogued and don't require UNIT or SPACE on the DD:

jcl
//VSAMFILE DD  DSN=MY.KSDS.CLUSTER,DISP=SHR
//VSAMOUT  DD  DSN=MY.OUTPUT.KSDS,DISP=(NEW,CATLG,DELETE)

VSAM data sets are created with IDCAMS (see above) and must be defined before use.


JCL Best Practices

Naming conventions:

  • Use meaningful job and step names: PAYROLL, SORTSTEP, RPTGEN
  • Prefix with your department or application: //FINRPT01 JOB

Defensive coding:

  • Always include COND= tests to stop a job on failure rather than cascading errors downstream
  • Use DISP=(NEW,CATLG,DELETE) — never leave abnormal-disp blank; default is system-dependent
  • Specify REGION=0M unless you have a specific reason to limit memory

Temporary data sets:

  • Use &&TEMPNAME for temporary files passed between steps — z/OS cleans them up at job end
  • Use PASS disposition to pass them between steps

Documentation:

  • Add //* comment statements before each step explaining its purpose
  • Keep procedures parameterised so they can be reused without modification

JCL Quick Reference Card

text
//jobname  JOB  (acct),'name',CLASS=,MSGCLASS=,MSGLEVEL=(1,1),NOTIFY=&SYSUID
//stepname EXEC PGM=program,PARM='value',COND=(cc,op,step)
//ddname   DD   DSN=dsname,DISP=(status,normal,abend),
//              UNIT=SYSDA,SPACE=(TRK,(pri,sec)),
//              DCB=(RECFM=FB,LRECL=80,BLKSIZE=8000)
//         DD   SYSOUT=*
//         DD   *
  in-stream data
/*
//*  Comment statement

What to Learn Next

Once you're comfortable with core JCL, the natural progression is:

  • COBOL — the most common language running under JCL on z/OS. See our COBOL Programming Tutorial for a complete guide.
  • VSAM — the primary file system for mainframe applications. Understanding KSDS, ESDS, and RRDS data sets is essential for real-world work.
  • JCL Interview Questions — if you're preparing for a mainframe developer interview, work through our 50 JCL Interview Questions.
  • Mainframe Career — explore salaries and demand in our Mainframe Developer Salary Guide 2026.