Appendix B: JCL Quick Reference

JCL is the language of the mainframe batch infrastructure. Every COBOL program that runs in batch does so because JCL told the system what to run, where to find it, what files to allocate, and what to do when something goes wrong. This reference covers what you need to write, read, and debug JCL in a production environment.

All JCL statements begin in column 1 with //. Comments begin with //*. Continuation requires breaking after a comma, coding // in columns 1-2 of the next line, and resuming the parameter in column 16 (or after at least one space following //).


JOB Statement

The JOB statement identifies the job to JES and specifies job-level parameters.

//JOBNAME  JOB (ACCT,INFO),'PROGRAMMER NAME',
//             CLASS=A,
//             MSGCLASS=X,
//             MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             REGION=0M,
//             TIME=1440,
//             TYPRUN=SCAN

Key JOB Parameters

Parameter Purpose Common Values
CLASS Job class (determines initiator) A-Z, 0-9; installation-defined
MSGCLASS Output class for job log X (held), A (print), installation-defined
MSGLEVEL Job log detail (1,1) = all JCL + all messages (use this for debugging)
NOTIFY TSO user to notify on completion &SYSUID (yourself)
REGION Memory limit 0M (unlimited, up to installation max)
TIME CPU time limit 1440 (no limit), (mm,ss), or NOLIMIT
TYPRUN Special processing SCAN (syntax check only), HOLD, COPY
RESTART Restart from a step stepname or procstep.stepname
COND Job-level condition code test Deprecated; use IF/THEN/ELSE
SCHENV WLM scheduling environment Installation-defined

REGION=0M requests the maximum region size the installation allows. In practice, most COBOL batch jobs need far less. Some shops set job-class defaults and prohibit REGION=0M in production.

TYPRUN=SCAN is your best friend when writing new JCL. It checks syntax without actually running anything. Always scan first.


EXEC Statement

The EXEC statement identifies a program or procedure to execute within a step.

//* Execute a program directly
//STEP01   EXEC PGM=MYPROG,PARM='INPUT=PROD'

//* Execute a cataloged procedure
//STEP02   EXEC PROC=MYPROC
//STEP02   EXEC MYPROC          (PROC= is optional)

//* With overrides
//STEP03   EXEC PGM=MYPROG,
//             REGION=256M,
//             TIME=30,
//             COND=(4,LT)

Key EXEC Parameters

Parameter Purpose Notes
PGM Program name Load module from STEPLIB or link list
PROC Procedure name From JCLLIB or system proclib
PARM Parameter string passed to program Max 100 characters; passed via PARM on EXEC
REGION Step memory limit Overrides JOB-level REGION
TIME Step CPU time limit Overrides JOB-level TIME
COND Condition code test for step bypass Deprecated; use IF/THEN/ELSE
ACCT Step-level accounting Installation-defined

PARM encoding: The system passes the PARM string to the program as a halfword length prefix followed by the character data. In COBOL, the Linkage Section receives it:

       LINKAGE SECTION.
       01  LS-PARM.
           05  LS-PARM-LENGTH    PIC S9(04) COMP.
           05  LS-PARM-DATA      PIC X(100).
       PROCEDURE DIVISION USING LS-PARM.

DD Statement

The DD (Data Definition) statement defines data sets and I/O resources for a step.

//* Existing sequential data set
//INPUTDD  DD DSN=PROD.DAILY.EXTRACT,
//            DISP=SHR

//* New sequential data set
//OUTPUTDD DD DSN=PROD.DAILY.REPORT,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(50,10),RLSE),
//            DCB=(RECFM=FB,LRECL=133,BLKSIZE=0),
//            UNIT=SYSDA

//* Temporary data set
//TEMPDD   DD DSN=&&TEMPFILE,
//            DISP=(NEW,PASS),
//            SPACE=(TRK,(100,50)),
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)

//* In-stream data
//SYSIN    DD *
  SELECT FIELDS=(1,10,CH,A)
/*

//* Concatenation
//INPUT    DD DSN=PROD.FILE.PART1,DISP=SHR
//         DD DSN=PROD.FILE.PART2,DISP=SHR
//         DD DSN=PROD.FILE.PART3,DISP=SHR

//* SYSOUT (print/spool)
//SYSPRINT DD SYSOUT=*

//* DUMMY (no I/O)
//OPTIONAL DD DUMMY

DSN (Data Set Name)

  • Maximum 44 characters
  • Qualifiers separated by periods, each max 8 characters
  • &&name creates a temporary data set (exists only for job duration)
  • *.stepname.ddname refers back to a data set from a previous step
//* Back-reference to a data set from a previous step
//INPUTDD  DD DSN=*.STEP01.OUTPUTDD,DISP=SHR

DISP (Disposition)

Format: DISP=(status,normal-end,abnormal-end)

Status (before step):

Value Meaning
NEW Create new data set
OLD Existing, exclusive access
SHR Existing, shared access
MOD Existing (extend) or new (create)

Normal termination disposition:

Value Meaning
DELETE Delete the data set
KEEP Keep but don't catalog
CATLG Keep and catalog
PASS Pass to a subsequent step
UNCATLG Uncatalog but keep on volume

Abnormal termination disposition:

Same values as normal. Defaults: if you code DISP=SHR, the default is (SHR,KEEP,KEEP). If you code DISP=(NEW,CATLG), the abnormal default is the same as normal — (NEW,CATLG,CATLG), which often is not what you want. For new data sets, code the abnormal disposition explicitly:

//OUTPUT   DD DSN=PROD.FILE,DISP=(NEW,CATLG,DELETE)

This ensures the data set is cleaned up if the step abends.

SPACE

Format: SPACE=(unit,(primary,secondary,directory),RLSE,,type)

Unit Meaning
TRK Tracks (~56 KB per track on 3390)
CYL Cylinders (~840 KB per cylinder on 3390)
nnn Average block size in bytes
//* 50 cylinders primary, 10 secondary, release unused
SPACE=(CYL,(50,10),RLSE)

//* PDS with 20 directory blocks
SPACE=(TRK,(200,50,20))

RLSE releases unused space at CLOSE. Always code it for output data sets — without it, you waste DASD.

Secondary allocation can occur up to 15 times per volume. If you exceed that, the job abends with Sx37 (out of space). Size your primary allocation generously.

DCB (Data Control Block)

DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
Parameter Values Meaning
RECFM F, FB, V, VB, FBA, VBA, U Record format. F=fixed, V=variable, B=blocked, A=ASA control char, U=undefined
LRECL 1-32760 Logical record length
BLKSIZE 0 or explicit Block size. 0 = system-determined (optimal). Always use 0 for new data sets.
DSORG PS, PO, DA Data set organization (usually not needed)

BLKSIZE=0 lets SMS (Storage Management Subsystem) or the system calculate the optimal block size. This is almost always what you want. Hard-coding BLKSIZE is a legacy practice from the days of physical tape handling.

UNIT and VOL

//OUTPUT   DD DSN=PROD.FILE,DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(100,20),RLSE),
//            UNIT=SYSDA,
//            VOL=SER=PROD01

UNIT=SYSDA is the generic DASD unit. UNIT=TAPE for tape. Most SMS-managed environments don't require explicit UNIT or VOL — SMS assigns them based on data class and storage group. Only code them when you have a specific reason.


JCLLIB and PROCs

JCLLIB

//MYJOB    JOB ...
//         JCLLIB ORDER=(MY.PROC.LIB,
//                       SHARED.PROC.LIB)

JCLLIB tells JES where to find cataloged procedures and include members. It must appear immediately after the JOB statement, before any EXEC. Multiple libraries are searched in order.

Cataloged Procedures

A PROC is a pre-written set of JCL steps stored in a procedure library.

//* In the proclib:
//COBCL    PROC SRCLIB='DEV.SOURCE.LIB',
//              MEMBER='MYPROG'
//COB      EXEC PGM=IGYCRCTL,PARM='OPT(2),LIST'
//STEPLIB  DD DSN=IGY.V6R4M0.SIGYCOMP,DISP=SHR
//SYSIN    DD DSN=&SRCLIB(&MEMBER),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSLIN   DD DSN=&&OBJMOD,DISP=(MOD,PASS),
//            SPACE=(TRK,(10,5)),UNIT=SYSDA
//LKED     EXEC PGM=IEWL,PARM='LIST,MAP',
//            COND=(8,LT,COB)
//SYSLIN   DD DSN=&&OBJMOD,DISP=(OLD,DELETE)
//SYSLMOD  DD DSN=MY.LOAD.LIB(&MEMBER),DISP=SHR
//SYSPRINT DD SYSOUT=*
//         PEND

Invoking and Overriding PROCs

//* Invoke with symbolic parameter overrides
//STEP01   EXEC COBCL,SRCLIB='PROD.SOURCE.LIB',
//              MEMBER='BATCHPGM'

//* Override a DD within a proc step
//COB.SYSIN DD DSN=MY.SPECIAL.SOURCE(MYPROG),DISP=SHR

//* Add a DD to a proc step
//LKED.EXTRADD DD DSN=MY.EXTRA.OBJ,DISP=SHR

The syntax for overriding is procstep.ddname. This is the single most confusing aspect of JCL for newcomers. The procstep name is the step name inside the proc, not the step name in your JCL.


IF/THEN/ELSE/ENDIF

//* Skip step if previous step had RC > 0
//         IF (STEP01.RC = 0) THEN
//STEP02   EXEC PGM=NEXTPROG
//INPUT    DD DSN=PROD.FILE,DISP=SHR
//         ENDIF

//* Complex condition
//         IF (STEP01.RC <= 4 AND
//             STEP02.RC = 0) THEN
//STEP03   EXEC PGM=FINALPGM
//         ELSE
//ERRSTEP  EXEC PGM=ERRHANDL
//         ENDIF

//* Test abend
//         IF (STEP01.ABEND) THEN
//RECOVER  EXEC PGM=RECOVERY
//         ENDIF

//* Test for specific abend code
//         IF (STEP01.ABENDCC = S0C7) THEN
//FIXDATA  EXEC PGM=DATAFIX
//         ENDIF

Operators: =, != (or <> or ^=), >, <, >=, <=, AND, OR, NOT

Testable conditions: stepname.RC, stepname.ABEND, stepname.ABENDCC, stepname.RUN, stepname.procstep.RC

IF/THEN/ELSE completely replaces the old COND parameter. There is no reason to use COND in new JCL. COND tests are backwards (the condition is when to skip), and they confuse everyone. Use IF/THEN/ELSE.


SET and Symbolic Parameters

//* SET statement for symbolic parameters
//         SET ENV=PROD
//         SET HLQ=PROD.DAILY

//STEP01   EXEC PGM=MYPROG
//INPUT    DD DSN=&HLQ..EXTRACT,DISP=SHR
//OUTPUT   DD DSN=&HLQ..REPORT,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(50,10),RLSE)

Note the double period in &HLQ..EXTRACT — the first period terminates the symbolic, the second is the DSN qualifier separator. This is a common source of JCL errors.


INCLUDE and JCLLIB

//         JCLLIB ORDER=(MY.JCL.LIB)
//         INCLUDE MEMBER=STDALLOC

INCLUDE pulls in a member from the JCLLIB, similar to COBOL's COPY. Use it for standard DD allocations that appear across many jobs.


Common Utility Programs

IDCAMS (Access Method Services)

The Swiss army knife of VSAM and catalog management.

//* Define a VSAM KSDS
//DEFCLUST EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DEFINE CLUSTER(                         -
           NAME(PROD.CUSTOMER.KSDS)       -
           INDEXED                        -
           RECORDSIZE(200 500)            -
           KEYS(10 0)                     -
           SHAREOPTIONS(2 3)              -
           CYLINDERS(100 20))             -
         DATA(NAME(PROD.CUSTOMER.KSDS.DATA)) -
         INDEX(NAME(PROD.CUSTOMER.KSDS.INDEX))
/*

//* Delete a data set (ignore "not found" errors)
//DELETE   EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DELETE PROD.OLD.FILE PURGE
  SET MAXCC = 0
/*

//* REPRO (copy data into VSAM)
//REPRO    EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//INDD     DD DSN=PROD.SEQ.FILE,DISP=SHR
//OUTDD    DD DSN=PROD.CUSTOMER.KSDS,DISP=SHR
//SYSIN    DD *
  REPRO INFILE(INDD) OUTFILE(OUTDD)
/*

//* LISTCAT
//LISTCAT  EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  LISTCAT ENTRIES(PROD.CUSTOMER.KSDS) ALL
/*

//* VERIFY (reset VSAM after abend)
//VERIFY   EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//MYFILE   DD DSN=PROD.CUSTOMER.KSDS,DISP=SHR
//SYSIN    DD *
  VERIFY FILE(MYFILE)
/*

SET MAXCC = 0 after DELETE is a standard pattern: it prevents a "data set not found" condition (which returns RC=8) from failing the job. This makes the DELETE idempotent.

IEBGENER (Sequential Copy)

//COPY     EXEC PGM=IEBGENER
//SYSUT1   DD DSN=INPUT.FILE,DISP=SHR
//SYSUT2   DD DSN=OUTPUT.FILE,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(10,5),RLSE),
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
//SYSPRINT DD SYSOUT=*
//SYSIN    DD DUMMY

SYSIN DD DUMMY means a straight copy with no reformatting. IEBGENER is the simplest way to copy a sequential data set. For anything more complex, use DFSORT or ICETOOL.

IEFBR14 (Do Nothing)

//* Create or delete data sets without running a program
//ALLOC    EXEC PGM=IEFBR14
//NEWFILE  DD DSN=PROD.NEW.FILE,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(50,10),RLSE),
//            DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
//OLDFILE  DD DSN=PROD.OLD.FILE,
//            DISP=(OLD,DELETE,DELETE)

IEFBR14 is a one-instruction program (BR 14 = return). It exists solely to give JCL a program to execute so that DD statements are processed. You use it to allocate, delete, or catalog data sets without running real code.

DFSORT

//SORT     EXEC PGM=SORT
//SORTIN   DD DSN=INPUT.FILE,DISP=SHR
//SORTOUT  DD DSN=OUTPUT.FILE,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(50,10),RLSE)
//SYSOUT   DD SYSOUT=*
//SYSIN    DD *
  SORT FIELDS=(1,10,CH,A,11,5,ZD,D)
  INCLUDE COND=(20,2,CH,EQ,C'NY')
  OUTREC FIELDS=(1,10,21,30,80:X)
  OUTFIL REMOVECC,
    HEADER1=('CUSTOMER REPORT',5X,DATE=(4MD/)),
    TRAILER1=('TOTAL RECORDS: ',COUNT)
/*

DFSORT control statements:

Statement Purpose
SORT FIELDS Sort keys: (position,length,format,order)
MERGE FIELDS Merge pre-sorted inputs
INCLUDE COND Include records matching condition
OMIT COND Exclude records matching condition
INREC Reformat records before sort
OUTREC Reformat records after sort
OUTFIL Multiple output files, reporting
SUM FIELDS Summarize (eliminate duplicates, sum numeric fields)

Format types: CH (character), ZD (zoned decimal), PD (packed decimal), BI (binary), FI (fixed-point integer), AC (ASCII character)

ICETOOL

//TOOL     EXEC PGM=ICETOOL
//TOOLMSG  DD SYSOUT=*
//DFSMSG   DD SYSOUT=*
//INPUT    DD DSN=PROD.TRANS.FILE,DISP=SHR
//REPORT   DD SYSOUT=*
//SUBSET   DD DSN=PROD.SUBSET.FILE,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(10,5),RLSE)
//TOOLIN   DD *
  COPY FROM(INPUT) TO(SUBSET) USING(CTL1)
  DISPLAY FROM(INPUT) LIST(REPORT) -
    TITLE('Transaction Summary') -
    HEADER('Account') ON(1,10,CH) -
    HEADER('Amount')  ON(15,9,ZD,A2) -
    HEADER('Count')   ON(VALCNT) -
    BLANK -
    HEADER('Total')   ON(15,9,ZD,A2,TOTAL)
//CTL1CNTL DD *
  INCLUDE COND=(25,8,CH,EQ,C'20260317')
/*

ICETOOL wraps DFSORT with higher-level operations: COPY, SORT, SELECT, DISPLAY, COUNT, STATS, RANGE, VERIFY. The DISPLAY operation is particularly powerful for ad-hoc reporting without writing a program.


JES2 JECL (Job Entry Control Language)

/*JOBPARM SYSAFF=SY1,LINES=9999
/*ROUTE PRINT R001
/*ROUTE XEQ  NODE1
/*PRIORITY 6
/*OUTPUT  FORMS=STD,COPIES=2
Statement Purpose
/*JOBPARM Job parameters (system affinity, output limits)
/*ROUTE PRINT Route printed output
/*ROUTE XEQ Route job execution to a specific node
/*PRIORITY Scheduling priority (0-15)
/*OUTPUT Output processing options
/*XMIT Transmit job to another node

SYSAFF=SY1 forces the job to run on system SY1 in a sysplex. Use it when a job requires resources available only on a specific LPAR. Avoid it when possible — it reduces workload balancing flexibility.


OUTPUT JCL Statement

//MYOUT    OUTPUT DEST=RMT001,
//             COPIES=3,
//             FORMS=INVOICE,
//             CLASS=A,
//             DEFAULT=YES

The OUTPUT statement provides finer control over printed/spooled output. DEFAULT=YES applies these options to all SYSOUT DDs in the step that don't specify their own output descriptor.


GDG (Generation Data Groups)

//* Define a GDG base (one-time setup via IDCAMS)
//DEFGDG   EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN    DD *
  DEFINE GDG(NAME(PROD.DAILY.EXTRACT) -
             LIMIT(30) -
             NOEMPTY -
             SCRATCH)
/*

//* Write to a new generation
//NEWGEN   DD DSN=PROD.DAILY.EXTRACT(+1),
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(100,20),RLSE),
//            DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)

//* Read the current (most recent) generation
//CURGEN   DD DSN=PROD.DAILY.EXTRACT(0),DISP=SHR

//* Read the previous generation
//PREVGEN  DD DSN=PROD.DAILY.EXTRACT(-1),DISP=SHR

GDGs provide automatic versioning. (+1) creates the next generation, (0) is the current, (-1) is the one before. The LIMIT controls how many generations are kept. SCRATCH means the oldest generation's DASD space is released when it rolls off.


Common DD Names

These DD names are conventions (some enforced by specific programs):

DD Name Used By Purpose
STEPLIB Loader Program search library (step-level)
JOBLIB Loader Program search library (job-level)
SYSPRINT Most programs Primary print output
SYSOUT DFSORT Sort messages
SYSIN Many utilities Control statements
SYSUT1 IEBGENER Input
SYSUT2 IEBGENER Output
SYSUDUMP System Formatted dump on abend
SYSABEND System Formatted dump (more detail)
CEEDUMP LE Language Environment dump
SORTIN DFSORT Sort input
SORTOUT DFSORT Sort output
SORTWKnn DFSORT Sort work files
SYSDBOUT Debug Tool Debug output
SYSDEBUG Compiler Debug side file

Return Code Conventions

RC Meaning
0 Successful, no issues
4 Warning (completed with minor issues)
8 Error (significant issues but some processing completed)
12 Severe error (minimal or no useful processing)
16 Critical error (no processing)

These are conventions, not standards. Your programs should follow them, and your JCL IF/THEN/ELSE tests should be based on them. A common pattern:

//         IF (STEP01.RC > 4) THEN
//NOTIFY   EXEC PGM=SENDMAIL,PARM='STEP01 FAILED'
//         ENDIF

Debugging JCL Checklist

When a job fails, check in this order:

  1. JESMSGLG — System messages, including security (RACF) failures
  2. JESJCL — Expanded JCL (with all symbolics resolved and procs expanded)
  3. JESYSMSG — Allocation messages (look for IEF244I, IEF272I, IEF285I)
  4. Step condition codes — In JESMSGLG, look for IEF142I messages
  5. SYSPRINT — Program-level messages
  6. SYSOUT — Additional program output
  7. CEEDUMP — Language Environment dump (if LE abend)
  8. SYSUDUMP/SYSABEND — System dump (if system abend)

Common JCL errors:

Error Message Cause
JCL ERROR IEFC621I Syntax error — check continuation, spelling, commas
NOT FOUND IEF212I Data set does not exist or is not cataloged
NOT CATLGD 2 IGD17101I Data set already exists (duplicate catalog entry)
ABEND S913 RACF security violation
ABEND S806 Program not found in STEPLIB/JOBLIB/link list
ABEND Sx37 Out of space

JCL Templates

//COMPILE  JOB (ACCT),'COMPILE',CLASS=A,
//             MSGCLASS=X,MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID
//         JCLLIB ORDER=(SYS1.PROCLIB)
//*
//COB      EXEC PGM=IGYCRCTL,
//             PARM='OPT(2),FASTSRT,RENT,OFFSET,LIB'
//STEPLIB  DD DSN=IGY.V6R4M0.SIGYCOMP,DISP=SHR
//SYSIN    DD DSN=MY.SOURCE.LIB(MYPROG),DISP=SHR
//SYSLIB   DD DSN=MY.COPY.LIB,DISP=SHR
//         DD DSN=SHARED.COPY.LIB,DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSLIN   DD DSN=&&OBJMOD,DISP=(MOD,PASS),
//            SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT1   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT2   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT3   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT4   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT5   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT6   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//SYSUT7   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//*
//LKED     EXEC PGM=IEWL,PARM='LIST,MAP,RENT',
//             COND=(8,LT,COB)
//SYSLIN   DD DSN=&&OBJMOD,DISP=(OLD,DELETE)
//SYSLMOD  DD DSN=MY.LOAD.LIB(MYPROG),DISP=SHR
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD SPACE=(CYL,(1,1)),UNIT=SYSDA
//*
//GO       EXEC PGM=*.LKED.SYSLMOD,
//             COND=(8,LT)
//STEPLIB  DD DSN=MY.LOAD.LIB,DISP=SHR
//SYSPRINT DD SYSOUT=*
//INPUT    DD DSN=MY.TEST.INPUT,DISP=SHR
//OUTPUT   DD SYSOUT=*

Standard Batch Run

//BATCHRUN JOB (ACCT),'DAILY BATCH',CLASS=A,
//             MSGCLASS=X,MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID,
//             REGION=0M
//*
//STEP01   EXEC PGM=BATCHPGM
//STEPLIB  DD DSN=PROD.LOAD.LIB,DISP=SHR
//INPUT    DD DSN=PROD.DAILY.EXTRACT(0),DISP=SHR
//OUTPUT   DD DSN=PROD.DAILY.REPORT(+1),
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(100,20),RLSE),
//            DCB=(RECFM=FB,LRECL=200,BLKSIZE=0)
//SYSPRINT DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//CEEDUMP  DD SYSOUT=*

Always allocate SYSUDUMP (or SYSABEND) and CEEDUMP in production JCL. When a program abends at 2 AM, the dump is the difference between a 10-minute fix and a 2-hour blind investigation.