JCL Procedures (PROCs)
Learn what JCL procedures are, the difference between cataloged and in-stream PROCs, symbolic parameters, and how to override a PROC's built-in settings from the calling job.
Introduction
By now you have written enough JCL to notice a pattern: certain blocks of steps and DD statements — compile a COBOL program, sort a file a particular way, run a standard backup — get used over and over, across many different jobs, with only minor details changing each time. Retyping that JCL every time would be tedious and error-prone. JCL procedures, or PROCs, solve exactly this problem by letting a block of JCL be written once and reused by name, with just the specific details that vary supplied at call time.
Why PROCs Exist
Imagine a shop compiles dozens of COBOL programs every day. Each compile involves several steps: the compiler itself, a link-edit step to build an executable load module, and various supporting DD statements for compiler listings, work datasets, and libraries. Coding all of that from scratch, correctly, in every single job that compiles a program would be both wasteful and risky — a single typo repeated across hundreds of jobs is a real operational hazard. A PROC captures that entire pattern once, tested and correct, so every job that needs to compile a COBOL program simply invokes it by name.
PROCs turn "copy, paste, and hope you did not typo anything" into "call a known-good, centrally maintained block of JCL by name." When the standard compile process needs to change shop-wide, it can be updated in exactly one place — the PROC — rather than in every job that uses it.
Cataloged vs. In-Stream PROCs
There are two ways a PROC can exist. A cataloged procedure is stored as a member of a partitioned dataset (a procedure library, commonly concatenated under the system's or a shop's PROCLIB), and can be invoked by name from any job without that job containing any of the procedure's actual JCL text. An in-stream procedure, by contrast, is coded directly inside the job stream that uses it, between PROC and PEND statements, and is only available to that one job.
| Type | Where It Lives | Typical Use |
|---|---|---|
| Cataloged PROC | A member of a procedure library (PROCLIB), shared shop-wide | Standard, widely reused patterns: compiles, backups, common reports |
| In-stream PROC | Coded inline within one job, between PROC and PEND | One-off jobs that call the same block of JCL more than once internally, or for learning/testing before cataloging something permanently |
//STEP010 EXEC COBCOMP//MYPROC PROC//STEP010 EXEC PGM=SORT//SORTIN DD DSN=&DSIN,DISP=SHR//SORTOUT DD DSN=&DSOUT,DISP=(NEW,CATLG,DELETE),// SPACE=(CYL,(5,5))// PEND//RUN1 EXEC MYPROC,DSIN=PROD.A.RAW,DSOUT=PROD.A.SORTED//RUN2 EXEC MYPROC,DSIN=PROD.B.RAW,DSOUT=PROD.B.SORTEDClick Run to see what this code prints.
Symbolic Parameters
A symbolic parameter is a placeholder, written with a leading ampersand (such as &DSIN in the example above), used inside a PROC wherever a value needs to vary between different invocations. When the PROC is called, the calling EXEC statement supplies actual values for those symbols, and z/OS substitutes them into the PROC's JCL text before the step actually runs. Symbolics can also have default values coded directly on the PROC statement, used automatically whenever a caller does not override them.
//COBCOMP PROC CLASS=B//COMPILE EXEC PGM=IGYCRCTL,REGION=0M//STEPLIB DD DSN=IGY.SIGYCOMP,DISP=SHR//SYSIN DD DSN=&SRCLIB(&MBR),DISP=SHR//SYSPRINT DD SYSOUT=*// PENDCalling a PROC and Overriding It
When invoking a PROC, the calling job supplies symbolic values as keyword parameters directly on the EXEC statement, in the form symbol=value, separated by commas. Any symbolic left unsupplied simply uses its coded default (if the PROC defines one) or is left unresolved, which typically causes a JCL error if the PROC actually needs a value for it.
//STEP010 EXEC COBCOMP,SRCLIB='PROD.COBOL.SOURCE',MBR=PAYRCALCOverriding a DD Statement Inside a PROC
Sometimes what needs to change between calls is not just a symbolic value, but an entire DD statement's parameters — for example, pointing SYSPRINT somewhere other than the PROC's default. This is done by coding a DD statement in the calling job with the same ddname as the one inside the PROC that should be overridden, using the special dot-qualified stepname.ddname reference when the PROC contains more than one step.
//STEP010 EXEC COBCOMP,SRCLIB='PROD.COBOL.SOURCE',MBR=PAYRCALC//COMPILE.SYSPRINT DD DSN=PROD.COMPILE.LISTINGS(PAYRCALC),// DISP=SHRClick Run to see what this code prints.
A Complete In-Stream PROC Example
//BACKUPJ JOB (ACCT77),'M PATEL',CLASS=B,MSGCLASS=X//BKUPPROC PROC DSIN=,DSOUT=//STEP010 EXEC PGM=IEBGENER//SYSUT1 DD DSN=&DSIN,DISP=SHR//SYSUT2 DD DSN=&DSOUT,DISP=(NEW,CATLG,DELETE),// SPACE=(CYL,(10,5)),DCB=*.SYSUT1//SYSPRINT DD SYSOUT=*//SYSIN DD DUMMY// PEND//RUN1 EXEC BKUPPROC,DSIN=PROD.CUSTMAST.KSDS,// DSOUT=PROD.CUSTMAST.BACKUPClick Run to see what this code prints.
Common Mistakes
- Forgetting the PEND statement when defining an in-stream PROC, which leaves the procedure definition unterminated.
- Overriding a DD statement with the wrong stepname.ddname qualifier when a PROC has multiple steps, silently affecting the wrong step or none at all.
- Assuming a symbolic parameter has a sensible default when the PROC does not define one — omitting a required value causes a JCL error.
- Editing a shared, cataloged PROC directly to fix a one-off need instead of overriding it from the calling job, which can break every other job that uses it.
Best Practices
- Prefer cataloged PROCs for anything reused across multiple jobs or by multiple people; reserve in-stream PROCs for genuinely one-off, job-local repetition.
- Give symbolic parameters clear, descriptive names (SRCLIB, MBR) rather than cryptic abbreviations, since other people will need to call your PROCs correctly.
- Document a PROC's expected symbolics and their meaning in comments near its PROC statement.
- Override from the calling job rather than editing a shared PROC for a one-time need — it keeps the shared logic stable for everyone else.
Frequently Asked Questions
As members of a partitioned dataset, commonly concatenated under a system or shop-defined PROCLIB. Which libraries JES searches for procedures is an installation-level configuration decision.
A PROC's steps run programs, not other PROCs directly, but nested procedure-calling patterns exist in some environments through more advanced JCL techniques. For learning purposes, think of a PROC as a fixed block of steps and DD statements, not something that itself further delegates to another named PROC.
The PROC's own coded DD statement is used exactly as written. Overrides are entirely optional and only needed when the calling job requires different data or output than the PROC's default.
No. PARM passes runtime data into a running program itself. A symbolic parameter is resolved during JCL processing, before execution, substituting text directly into the JCL statements of a called PROC.
Key Takeaways
- PROCs package reusable blocks of JCL so common patterns are written once and called by name many times.
- Cataloged PROCs live in a shared procedure library; in-stream PROCs are coded within a single job between PROC and PEND.
- Symbolic parameters (&NAME) let calling jobs supply varying values into an otherwise fixed procedure.
- Calling jobs can override PROC-level symbolics and even individual DD statements without modifying the shared PROC itself.
- Centralizing common JCL in PROCs reduces duplication and the risk of inconsistent, error-prone copy-pasted job streams.
Summary
PROCs are how mainframe shops avoid rewriting the same JCL patterns over and over, centralizing common logic while still allowing individual jobs to customize what they need through symbolic parameters and overrides. With JOB, EXEC, DD, and PROC now covered, you have a solid working command of core JCL. In the next lesson, you will look at the utility programs that JCL most commonly runs in production — IDCAMS for VSAM management and DFSORT/SORT for sorting and reformatting data.