cheat sheet
JCL Quick Reference
Job Control Language syntax, common statements, EXEC, DD, and SYSOUT patterns.
JCL Quick Reference
What it is
JCL (Job Control Language) is the scripting language used to submit batch jobs on IBM mainframes running z/OS, unchanged in its core syntax since the System/360 era in the 1960s and maintained by IBM as part of the z/OS base. Every production batch workload on z/OS — whether a COBOL program, a sort utility, or a data copy — runs through a JCL job that specifies the program to execute, the input and output datasets, resource requirements, and dependencies between steps. Reach for JCL when you need to submit, schedule, or chain batch programs on z/OS; for interactive automation or glue code between steps, REXX is more flexible.
Basic job structure
Every JCL job follows a fixed three-card sequence: a JOB card that identifies the job to the system, one or more EXEC cards that name the program or procedure to run, and DD cards that describe the datasets each step reads or writes. Continuation lines must start with // in columns 1–2 and a non-blank character in column 4 or later.
//MYJOB JOB (ACCT),'Alice Dev',CLASS=A,
// MSGCLASS=X,MSGLEVEL=(1,1),
// NOTIFY=&SYSUID
//*
//* Simple one-step job
//*
//STEP01 EXEC PGM=IEFBR14
//STEP02 EXEC PGM=IDCAMS
//SYSIN DD *
LISTCAT ENTRIES(MY.DATA.SET) ALL
/*
//SYSPRINT DD SYSOUT=*
Common statements
JOB statement
The JOB card is the first statement in every job and provides the job name, accounting information, and scheduling parameters. CLASS controls which initiator picks up the job, MSGCLASS sets where the job log is routed, and NOTIFY=&SYSUID sends a message to your TSO session when the job ends.
//jobname JOB accounting-info,
// programmer-name,
// CLASS=job-class,
// MSGCLASS=output-class,
// MSGLEVEL=(statements,messages),
// NOTIFY=&SYSUID,
// REGION=0M,
// TIME=1440
EXEC statement
PGM= runs a load module directly; PROC= (or just the proc name) expands a catalogued or in-stream procedure before running it. Each EXEC starts a new job step with its own return code; steps are numbered STEP01, STEP02, etc. by convention.
//stepname EXEC PGM=program-name
//stepname EXEC PGM=IKJEFT01,DYNAMNBR=30 (* TSO in batch *)
//stepname EXEC PROC=procname
//stepname EXEC procname,PARM.step='options'
DD statement patterns
A DD (Data Definition) card binds a logical file name used inside the program to a physical dataset on disk, tape, or SYSOUT. The ddname matches the DDNAME referenced in the program's OPEN call (e.g. SYSIN, SYSPRINT); DISP, DSN, SPACE, and DCB describe how the dataset is managed.
//* Sequential dataset
//INFILE DD DSN=MY.INPUT.FILE,
// DISP=SHR
//* New dataset
//OUTFILE DD DSN=MY.OUTPUT.FILE,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5)),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
// UNIT=SYSDA
//* PDS member
//SYSIN DD DSN=MY.PROC.LIB(MYMEMBER),DISP=SHR
//* Inline data
//SYSIN DD *
data here
/*
//* Sysout
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=A
//* Dummy (discard)
//SYSIN DD DUMMY
//* GDG (Generation Data Group)
//INFILE DD DSN=MY.GDG.FILE(0),DISP=SHR (* current *)
//INFILE DD DSN=MY.GDG.FILE(-1),DISP=SHR (* previous *)
//OUTFILE DD DSN=MY.GDG.FILE(+1),
// DISP=(NEW,CATLG,DELETE),...
DISP parameter
DISP is a three-part keyword that tells z/OS the current status of the dataset, what to do with it if the step ends normally, and what to do if the step abends. Omitting the second or third sub-parameter leaves the system to apply a default; the most common combination is (NEW,CATLG,DELETE) for a new dataset you want to keep only on success.
DISP=(status, normal-end, abnormal-end)
Status: NEW SHR OLD MOD
Normal-end: KEEP DELETE CATLG UNCATLG PASS
Abnormal-end: KEEP DELETE CATLG UNCATLG
IEFBR14 — do nothing
IEFBR14 is a one-instruction program that returns immediately with RC=0, making it useful as a no-op step when you only want JCL's DD-processing side effects — typically allocating a new dataset or deleting an existing one via DISP=(OLD,DELETE) — without running any real program logic.
//ALLOC EXEC PGM=IEFBR14
//NEWDS DD DSN=MY.NEW.DATASET,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(5,2)),
// DCB=(RECFM=FB,LRECL=80),
// UNIT=SYSDA
IDCAMS — VSAM utility
IDCAMS (Access Method Services) is the IBM utility for all VSAM dataset lifecycle operations — defining, copying, deleting, and cataloguing KSDS, ESDS, and RRDS clusters. It also handles non-VSAM tasks like REPRO (copy any sequential dataset) and LISTCAT (list the catalog), making it one of the most frequently executed utilities on z/OS.
//IDCAMS EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE CLUSTER -
(NAME(MY.VSAM.KSDS) -
RECORDS(1000 100) -
KEYS(10 0) -
RECORDSIZE(80 80)) -
DATA(NAME(MY.VSAM.KSDS.DATA)) -
INDEX(NAME(MY.VSAM.KSDS.INDEX))
IF LASTCC = 0 THEN DO
REPRO INFILE(INDD) OUTFILE(OUTDD)
END
DELETE MY.OLD.DATASET PURGE
/*
SET, IF/THEN/ELSE, COND
JCL provides limited procedural control: SET assigns a symbolic variable, COND on an EXEC card skips the step if a previous return code meets a condition, and the IF/THEN/ELSE/ENDIF construct (introduced in JCL for MVS/ESA) allows conditional step execution based on return codes or abend flags. Prefer IF/THEN/ELSE over COND for readability; they can be combined in the same job.
// SET MYVAR=VALUE
//STEP02 EXEC PGM=MYPGM,
// COND=(0,NE,STEP01) (* skip if STEP01 RC != 0 *)
// IF (STEP01.RC = 0) THEN
//GOOD EXEC PGM=GOODPATH
// ELSE
//BAD EXEC PGM=ERRPATH
// ENDIF
JOB card parameters
The JOB card is a single statement but accepts well over a dozen sub-parameters that govern scheduling, accounting, resource limits, and notification. Most shops standardise on a small subset — CLASS, MSGCLASS, MSGLEVEL, NOTIFY, REGION — and let installation defaults pick up the rest, but every parameter below shows up in production JCL eventually.
| Parameter | Meaning |
|---|---|
CLASS=x | JES initiator class (A–Z, 0–9). Controls which initiators are eligible to run the job; sites map classes to resource profiles. |
MSGCLASS=x | Output class for job log (JESMSGLG, JESJCL, JESYSMSG). X is typically held SYSOUT; A typically printed. |
MSGLEVEL=(s,m) | s = statements (0 = JOB only, 1 = JOB+procs expanded, 2 = JOB only no procs); m = messages (0 = JCL errors only, 1 = all). (1,1) is the common dev default. |
NOTIFY=&SYSUID | TSO userid to receive an end-of-job message. &SYSUID substitutes the submitter automatically. |
REGION=nnnM | Virtual storage above and below the 16 MB line. REGION=0M (or 0K) requests "all available" — common for sort and DB2 jobs. |
TIME=(m,s) | CPU time limit. TIME=1440 and TIME=NOLIMIT disable the limit; TIME=(2,30) is 2 min 30 sec. |
TYPRUN= | SCAN validates JCL syntax without running; HOLD queues the job in JES HOLD state; JCLHOLD holds for JCL processing only; COPY prints the JCL to SYSOUT and exits. |
COND=(rc,op[,step]) | Suppress remaining steps if a previous return code matches. Job-level form applies to every subsequent step. |
RESTART=stepname | Begin execution at stepname (typically used by automated restart tooling after an abend). |
PRTY=n | JES priority 0–15; higher runs sooner within the same class. |
USER= / PASSWORD= | Submit the job under a different RACF identity. PASSWORD= is uncommon in modern shops — surrogate authority is preferred. |
GROUP= | RACF connect group; only useful when the user is connected to more than one group. |
ADDRSPC=REAL | Force the job into non-pageable storage (rare, usually for I/O-sensitive workloads). |
BYTES= / LINES= / CARDS= / PAGES= | Output volume limits; exceeding any of them causes a S722 abend. |
SCHENV= | WLM scheduling environment — only initiators that satisfy this resource list will pick up the job. |
JESLOG= | SUPPRESS discards JES log datasets at job end; SPIN allows mid-job log spinning. |
ACCT= | Accounting information (free-form) — distinct from the positional accounting field. |
//ALICEJ01 JOB (BILLG,DEPT42),'Alice Dev',
// CLASS=A,
// MSGCLASS=X,
// MSGLEVEL=(1,1),
// NOTIFY=&SYSUID,
// REGION=0M,
// TIME=(5,0),
// TYPRUN=SCAN, (* validate only *)
// SCHENV=DB2PROD,
// USER=ALICE
EXEC card parameters
EXEC starts a job step and inherits or overrides many of the same resource limits set on the JOB card. The most-used keywords are PGM=/PROC=, PARM=, COND=, and REGION=; the rest let you fine-tune resource consumption per step.
| Parameter | Meaning |
|---|---|
PGM=name | Load module to execute. The system searches STEPLIB / JOBLIB / LNKLST. |
PROC=name (or name alone) | Catalogued or in-stream procedure name; the proc text is expanded into the job at conversion time. |
PARM='text' | Up to 100 bytes passed to the program in register 1. Quote if it contains commas or special characters. |
PARM.step='text' | When invoking a PROC, send PARM to a specific step inside the proc. |
COND=(rc,op,step) | Skip this step if the comparison is true. Multiple tests are AND-ed by default; EVEN/ONLY qualify whether to run on abend. |
COND=EVEN | Run even when an earlier step abended. |
COND=ONLY | Run ONLY when an earlier step abended (cleanup steps). |
REGION=nnnM | Override job-level REGION just for this step. |
TIME=(m,s) | Override job-level TIME. |
ACCT=(info) | Accounting info for this step. |
ADDRSPC=REAL | Force this step into non-pageable storage. |
DYNAMNBR=n | Max concurrent dynamic allocations — increase for TSO-in-batch (IKJEFT01) or REXX EXECs. |
PERFORM=n | Performance group (WLM legacy). |
//STEP01 EXEC PGM=ALICEPGM,
// PARM='OPTIONS=DEBUG,VERBOSE',
// REGION=128M,
// TIME=(2,30),
// COND=(4,LT,STEP00) (* skip if STEP00 RC > 4 *)
//STEP02 EXEC PGM=ALICECLEAN,
// COND=ONLY (* cleanup only after an abend *)
PARM= continuation
The PARM string is limited to 100 bytes after concatenation, but the JCL coding rule for splitting a quoted string across cards is non-obvious: close the quote at the end of one card, code // continuation, and reopen the quote at column 16 or later. The system concatenates the segments before passing them to the program.
//STEP01 EXEC PGM=ALICEPGM,
// PARM=('PART1=ALPHA,PART2=BETA,',
// 'PART3=GAMMA,PART4=DELTA')
Symbolic parameters
Symbolic parameters are placeholders in JCL that get substituted at conversion time. They take the form &NAME and are typically defined on a PROC statement (for parameterised procedures) or on a JOB-level SET statement. Symbolics let one piece of JCL serve many jobs without copy-paste editing — change one PARM and every step that references it picks up the new value.
| Form | Where defined | Override at |
|---|---|---|
&NAME on PROC statement | First card of a PROC | EXEC card that invokes the proc |
System symbolic &SYSUID, &SYSPLEX, &SYSNAME, &SYSCLONE | System parmlib | Cannot be overridden — always system-supplied |
// SET NAME=value | Anywhere before first use | Re-SET later in the job |
&NAME on EXEC PARM | Resolved before program sees the value | Override with PARM='&NAME' substitution |
//* In-stream PROC with defaults
//PROCNAME PROC HLQ=ALICE, (* default HLQ *)
// RUNDATE=&SYSDATE, (* system symbolic *)
// CLASS=A
//STEP01 EXEC PGM=ALICEPGM,REGION=64M
//INFILE DD DSN=&HLQ..DAILY.&RUNDATE,DISP=SHR
//OUTFILE DD SYSOUT=&CLASS
// PEND
//* Invoke with override
//RUN EXEC PROCNAME,HLQ=DEPT42,CLASS=H
Concatenation gotcha — the double dot
When a symbolic is followed immediately by text that itself starts with a ., JCL requires .. (two dots) so the substitution engine knows where the symbolic name ends. &HLQ.MYDATA is ALICE concatenated with MYDATA; &HLQ..MYDATA is ALICE.MYDATA. This trips up every newcomer at least once.
//* &HLQ resolved to ALICE
//WRONG DD DSN=&HLQ.DAILY,DISP=SHR (* ALICEDAILY — broken *)
//RIGHT DD DSN=&HLQ..DAILY,DISP=SHR (* ALICE.DAILY — correct *)
PROC — procedures
A PROC is a reusable block of JCL — one or more EXEC and DD statements wrapped in // PROC … // PEND (in-stream) or stored as a member in a PROCLIB and referenced by name. Catalogued procs live in SYS1.PROCLIB, site procs in shop-specific PDS libraries, and any PDS can be added with JCLLIB.
In-stream PROC
In-stream procs are defined inside the job between // PROC and // PEND, then invoked by the steps that follow. They are convenient for one-off parameterised pipelines.
//ALICEJ01 JOB (ACCT),'INSTREAM PROC',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//COPYDS PROC SRC=,TGT=
//STEP01 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSUT1 DD DSN=&SRC,DISP=SHR
//SYSUT2 DD DSN=&TGT,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(5,5)),
// DCB=(RECFM=FB,LRECL=80)
// PEND
//RUN1 EXEC COPYDS,SRC=ALICE.SOURCE,TGT=ALICE.COPY1
//RUN2 EXEC COPYDS,SRC=ALICE.SOURCE,TGT=ALICE.COPY2
Cataloged PROC + JCLLIB
For procs that live as PDS members, point the job at the proclib with JCLLIB ORDER=() before the first invocation. The search order is left-to-right; SYS1.PROCLIB is always implicitly last.
//ALICEJ02 JOB (ACCT),'CATLG PROC',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
// JCLLIB ORDER=(ALICE.PROCLIB,DEPT.PROCLIB)
// INCLUDE MEMBER=STDSETUP (* see INCLUDE below *)
//STEP01 EXEC PROC=DAILYLOAD,HLQ=ALICE,RUNDATE=20260525
Override a proc DD
To override a DD inside a proc, code the DD on the invoking job using stepname.ddname as the DD name. Both the override DD and the proc DD must use compatible attributes.
//RUN1 EXEC PROC=DAILYLOAD
//STEP01.INFILE DD DSN=ALICE.OVERRIDE.INPUT,DISP=SHR
//STEP01.OUTFILE DD DSN=ALICE.OVERRIDE.OUTPUT,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5))
INCLUDE statement
INCLUDE MEMBER=name pulls another PDS member into the current job at conversion time, before any steps run. The included member can hold DD overrides, SET statements, or whole step sequences; INCLUDE is commonly used for site-wide STEPLIB or RACF setup boilerplate so each job carries only its unique logic.
//ALICEJ03 JOB (ACCT),'WITH INCLUDE',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
// JCLLIB ORDER=(ALICE.INCLIB)
// INCLUDE MEMBER=DB2SETUP (* STEPLIB to DB2 load, SET DB2SSID=, … *)
// INCLUDE MEMBER=STDDDS (* common SYSOUT/SYSPRINT DDs *)
//STEP01 EXEC PGM=ALICEPGM
JOBLIB, STEPLIB, LNKLST
When PGM=name runs, z/OS searches for the load module in this order: STEPLIB (specific to the step), JOBLIB (entire job), LNKLST (system link list defined in SYS1.LINKLIB chain via LNKLSTxx parmlib). STEPLIB is the strongest scope and overrides JOBLIB and the system list.
//* JOBLIB applies to every step in this job
//ALICEJ04 JOB (ACCT),'JOBLIB EXAMPLE',CLASS=A,MSGCLASS=X
//JOBLIB DD DSN=ALICE.LOAD,DISP=SHR
// DD DSN=DEPT.SHARED.LOAD,DISP=SHR
//STEP01 EXEC PGM=ALICEPGM (* uses JOBLIB *)
//* STEPLIB overrides JOBLIB for this step only
//STEP02 EXEC PGM=ALICEPGM2
//STEPLIB DD DSN=TEST.LOAD,DISP=SHR
// DD DSN=ALICE.LOAD,DISP=SHR
A STEPLIB DD DUMMY or omitted STEPLIB falls through to JOBLIB; an empty JOBLIB falls through to LNKLST. Mixing PDS and PDSE in the same concatenation is allowed but PDSE must come first if its DSNTYPE=LIBRARY modules are to be picked up cleanly.
Continuation and column rules
JCL is a positional language with strict column rules inherited from punched-card days. Get them wrong and the JES2 converter rejects the card with IEFC605I UNIDENTIFIED OPERATION FIELD.
| Column(s) | Required |
|---|---|
| 1–2 | // for JCL, //* for comment, /* for delimiter |
| 3–10 | Name field (jobname, stepname, ddname) — left-justified, 1–8 chars |
| 12+ | Operation field (JOB, EXEC, DD, SET, PROC, …) |
| After operation | Operands, separated by commas, no embedded blanks |
| 72 | Continuation indicator — must be blank unless this is a continuation marker (rare) |
| 73–80 | Sequence area — ignored by the converter, traditionally used for line numbers |
Continuation: end the line with a comma after a complete operand, start the next line with // in columns 1–2 and the continuation in column 4–15. Indented continuations make multi-parameter DD statements far more readable.
//OUT DD DSN=ALICE.OUTPUT,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
Comment cards use //* and can appear anywhere except inside a continuation. They are stripped at conversion and do not affect operation.
//*--------------------------------------------------------------
//* This step builds the daily load file from the master.
//*--------------------------------------------------------------
//STEP01 EXEC PGM=ALICEPGM
Common utility programs
The IBM-supplied utility programs cover most non-application batch work — copy, sort, allocate, catalog, manage VSAM. Use this table as a jump-off; each utility has its own deep-dive article when one exists.
| Program | Purpose | Deep-dive |
|---|---|---|
IEFBR14 | No-op; used to allocate or delete via DD-only side effects | (above) |
IEBGENER | Copy a sequential dataset to another sequential dataset; supports record-level reformat | — |
IEBCOPY | Copy or compress a PDS / PDSE; unload to and load from sequential | iebcopy |
IDCAMS | VSAM lifecycle, catalog, REPRO, LISTCAT, ALTER | idcams |
SORT / ICETOOL | DFSORT — sort, merge, copy, filter, join | dfsort |
IKJEFT01 | TSO in batch — run REXX/CLIST or TSO commands from JCL | tso-ispf |
IKJEFT1B | Same as IKJEFT01 but does not abend on non-zero RC | tso-ispf |
IEBPTPCH | Print and punch (line printer formatted output of dataset) | — |
IEBUPDTE | Apply update records to a PDS or sequential dataset | — |
IEBCOMPR | Compare two sequential datasets or PDS members | — |
IEFUSI | Installation SMF exit (not invoked directly) | — |
IEHLIST | List VTOC contents and catalog entries | — |
IEHMOVE | Legacy move/copy — superseded by DFSMSdss | — |
ADRDSSU | DFSMSdss — disk dump/restore, copy, defrag | — |
A minimal IEBGENER copy with record-level reformat (the GENERATE/RECORD control statements):
//COPY EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1 DD DSN=ALICE.SOURCE.FB80,DISP=SHR
//SYSUT2 DD DSN=ALICE.TARGET.FB80,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(5,5),RLSE),
// DCB=(RECFM=FB,LRECL=80)
//SYSIN DD *
GENERATE MAXFLDS=1
RECORD FIELD=(10,1,,1) (* copy bytes 1-10 to position 1 *)
/*
See jcl-dd for the full DD-statement reference, including DCB, SPACE, DISP truth tables, GDG syntax, concatenation, DUMMY, and SMS classes.
Real-world recipes
Multi-step ETL pipeline with PASSed temp dataset
A daily extract job runs in three stages: pull from VSAM, sort, then load. The intermediate file is a job-temporary dataset (&&) passed between steps and deleted automatically at job end.
//ALICEJ05 JOB (ACCT),'DAILY ETL',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//* STEP01 - extract from VSAM
//*
//STEP01 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//IN DD DSN=ALICE.VSAM.MASTER,DISP=SHR
//OUT DD DSN=&&EXTRACT,
// DISP=(NEW,PASS),
// SPACE=(CYL,(50,10),RLSE),
// DCB=(RECFM=FB,LRECL=200)
//SYSIN DD *
REPRO INFILE(IN) OUTFILE(OUT)
/*
//*
//* STEP02 - sort by customer key
//*
//STEP02 EXEC PGM=SORT,COND=(0,NE,STEP01)
//SORTIN DD DSN=&&EXTRACT,DISP=(OLD,PASS)
//SORTOUT DD DSN=&&SORTED,
// DISP=(NEW,PASS),
// SPACE=(CYL,(50,10),RLSE),
// DCB=(RECFM=FB,LRECL=200)
//SYSOUT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=(1,12,CH,A)
/*
//*
//* STEP03 - load to daily target dataset (GDG)
//*
//STEP03 EXEC PGM=IEBGENER,COND=(0,NE,STEP02)
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSUT1 DD DSN=&&SORTED,DISP=(OLD,DELETE)
//SYSUT2 DD DSN=ALICE.DAILY.LOAD(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(100,20),RLSE),
// DCB=(RECFM=FB,LRECL=200)
Conditional restart-on-failure
A two-leg job runs the main batch, and if it abends, dispatches a notification step and an orderly cleanup step using COND=ONLY.
//ALICEJ06 JOB (ACCT),'RESTART LOGIC',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP01 EXEC PGM=ALICEMAIN,REGION=128M
//INFILE DD DSN=ALICE.INPUT,DISP=SHR
//OUTFILE DD DSN=ALICE.OUTPUT,
// DISP=(NEW,CATLG,KEEP),
// SPACE=(CYL,(50,10),RLSE),
// DCB=(RECFM=FB,LRECL=200)
//*
//* Notification step runs only on abend
//*
//STEP02 EXEC PGM=IEBGENER,COND=ONLY
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSUT1 DD *
ALICEJ06 ABENDED IN STEP01 - SEE SDSF
/*
//SYSUT2 DD SYSOUT=(*,INTRDR)
//*
//* Cleanup step also runs only on abend
//*
//STEP03 EXEC PGM=IEFBR14,COND=ONLY
//KILL DD DSN=ALICE.OUTPUT,DISP=(OLD,DELETE)
Parameterised PROC with overrides
A cataloged proc that loads any dataset into a daily GDG; the calling jobs pass the source dataset and skip date via symbolics, and one job overrides the OUTFILE allocation entirely.
//* Stored as ALICE.PROCLIB(DAILYLD)
//DAILYLD PROC SRC=,SKIP=0
//STEP01 EXEC PGM=ALICEPGM,PARM='SKIP=&SKIP'
//STEPLIB DD DSN=ALICE.LOAD,DISP=SHR
//INFILE DD DSN=&SRC,DISP=SHR
//OUTFILE DD DSN=ALICE.DAILY.LOAD(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(100,20),RLSE),
// DCB=(RECFM=FB,LRECL=200)
//SYSOUT DD SYSOUT=*
// PEND
//* Caller 1 - defaults
//ALICEJ07 JOB (ACCT),'DAILY 1',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
// JCLLIB ORDER=(ALICE.PROCLIB)
//RUN EXEC PROC=DAILYLD,SRC=ALICE.SRC.A
//* Caller 2 - override OUTFILE allocation
//ALICEJ08 JOB (ACCT),'DAILY 2',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
// JCLLIB ORDER=(ALICE.PROCLIB)
//RUN EXEC PROC=DAILYLD,SRC=ALICE.SRC.B,SKIP=1000
//STEP01.OUTFILE DD DSN=ALICE.WEEKLY.SUMMARY,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(500,100),RLSE),
// DCB=(RECFM=FB,LRECL=200)
GDG-aware backup with retention
A weekly backup keeps the last four generations; the GDG base is defined once with IDCAMS, then ordinary JCL allocates each new (+1) generation. RETPD=28 deletes generations after four weeks regardless of GDG limit.
//* One-time setup (run separately)
//SETUP EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE GENERATIONDATAGROUP -
(NAME(ALICE.WEEKLY.BACKUP) -
LIMIT(4) -
SCRATCH -
NOEMPTY)
/*
//* Weekly backup job
//ALICEJ09 JOB (ACCT),'WEEKLY BACKUP',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//STEP01 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSIN DD DUMMY
//SYSUT1 DD DSN=ALICE.PROD.MASTER,DISP=SHR
//SYSUT2 DD DSN=ALICE.WEEKLY.BACKUP(+1),
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(500,100),RLSE),
// DCB=(RECFM=FB,LRECL=200),
// RETPD=28
Compile-link-go pipeline
A classic three-step language pipeline: compile a COBOL source into an object, link the object into a load module, then execute it. The first two steps use cataloged IBM procs; the third is a hand-coded EXEC.
//ALICEJ11 JOB (ACCT),'CLG',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//*
//* Compile
//*
//COMP EXEC PROC=IGYWC,PARM.COBOL='LIST,MAP,XREF'
//COBOL.SYSIN DD DSN=ALICE.SRC.COBOL(ALICEPGM),DISP=SHR
//COBOL.SYSLIN DD DSN=&&OBJ,DISP=(NEW,PASS),
// SPACE=(CYL,(1,1)),
// DCB=(RECFM=FB,LRECL=80)
//*
//* Link-edit
//*
//LKED EXEC PROC=IEWL,COND=(0,LT,COMP)
//SYSLIN DD DSN=&&OBJ,DISP=(OLD,DELETE)
// DD DDNAME=SYSIN
//SYSLMOD DD DSN=ALICE.LOAD(ALICEPGM),DISP=SHR
//SYSIN DD *
NAME ALICEPGM(R)
/*
//*
//* Run
//*
//GO EXEC PGM=ALICEPGM,COND=(0,LT,LKED)
//STEPLIB DD DSN=ALICE.LOAD,DISP=SHR
//INFILE DD DSN=ALICE.SAMPLE.INPUT,DISP=SHR
//OUTFILE DD SYSOUT=*
TYPRUN=SCAN validation step
To validate a JCL change without actually running it, submit with TYPRUN=SCAN — the JES converter parses every card and emits JCL errors but no step executes. Useful in a CI pipeline that lints JCL on every commit.
//ALICEJ10 JOB (ACCT),'SCAN ONLY',CLASS=A,MSGCLASS=X,
// TYPRUN=SCAN,
// NOTIFY=&SYSUID
//STEP01 EXEC PGM=IEFBR14
//DD1 DD DSN=ALICE.WOULD.BE.NEW,
// DISP=(NEW,CATLG,DELETE),
// SPACE=(CYL,(10,5),RLSE),
// DCB=(RECFM=FB,LRECL=80)
Sources
References consulted while writing this article. Links open in a new tab.
- IBM Documentation — z/OS MVS JCL Reference — Authoritative reference for the JOB / EXEC / DD syntax and disposition/space/dcb parameters covered in this article.
Common pitfalls
- Lowercase outside quotes — JCL is case-sensitive at the token level:
Disp=Shrwill be flagged. Always uppercase keywords, dataset names, and values; only quoted strings preserve case. - Column-72 continuation — a stray character in column 72 turns into a continuation indicator and the next card is treated as a continuation. Many editors hide column 72; turn the column ruler on (
COLSin ISPF Edit). - Missing comma at end of continued line — a continuation line is recognised by the absence of a complete statement, signalled by ending the previous line with a comma after a complete operand. Forgetting the comma yields
IEFC662I"INVALID LABEL". - PROC override
stepname.ddnamemismatch — the stepname must match the step name inside the proc, not an arbitrary alias. Spell it the same way the proc does. - JCL error vs allocation error —
IEFC***messages come from the converter before any step runs (JCL syntax problem);IEC***andIGD***come from allocation at step start (real-world: dataset doesn't exist, RACF denied, volume full). Look at the message prefix to know which side of the fence the failure happened on. DISP=NEWon an existing dataset —IGD17001Ifor SMS,IEC141I 013for non-SMS. Either delete first withIEFBR14 + DISP=(OLD,DELETE), or useDISP=MODto append, orDISP=OLDto reuse.COND=polarity confusion —COND=(rc,op,step)skips the step when the comparison is true.COND=(0,NE,STEP01)reads "skip if STEP01's RC is not equal to 0", i.e. only run on success. UseIF/THEN/ELSEfor anything more complex than one test.- JCLLIB before INCLUDE —
JCLLIBmust precede the firstINCLUDEorEXEC PROC=reference. Otherwise the converter can't find the member. - Symbolic substitution and the double dot —
&HLQ.DATAconcatenates without a dot;&HLQ..DATAproducesHLQ.DATA. The same rule applies to every text-adjacent symbolic reference. - In-stream
/*inside data — the/*delimiter forDD *ends the in-stream block. If your data legitimately contains/*in column 1, useDD *,DLM=@@and end the block with@@, or useDD DATAwhich only terminates at//. REGION=0Mwithout site approval — some shops reject jobs above a certain region size at the JES2 init exit. Check your installation default before assuming "0M means unlimited."- Forgetting
JOBLIBis job-wide — aJOBLIB DDdefined in step 1 still applies to step 7. UseSTEPLIBfor step-scoped library overrides; don't put environment-specific libraries in JOBLIB. NOTIFY=&SYSUIDfailing on submitted-from-batch jobs —&SYSUIDresolves to the job's own userid, which may be a functional ID with no TSO session. Hardcode a real TSO id (NOTIFY=ALICE) for batch-of-batch.- GDG
(+1)referenced in a later step withoutDISP=PASS— the new generation is not catalogued until job end; step 2 sees the catalog as it was at job start. CodeDISP=(NEW,PASS)thenDISP=(OLD,CATLG). - Tape jobs with
EXPDT=99000— at some sites this is interpreted as "never scratch"; at others as a literal 1999 date. Use the unambiguousEXPDT=1999/365form.