Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

COBOL  Module 12 – Mainframe batch concepts: data sets, JCL and compilation (IBM z/OS)

JCL procedures, symbols and conditional steps

Run JCL steps on the return codes of earlier ones with IF/THEN/ELSE or COND, and turn steps into procedures whose symbols change per call.

  • Intermediate
  • 20 minutes
  • Examples run with GnuCOBOL 3.2, Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 7 of 20 minutes free
Free: A step's return code decides which steps run next · A procedure is a set of steps whose symbols each call can change · Practise: return codes, procedures and symbols. With a pass: 2 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Decide which steps of a job run from the return codes of earlier steps, with IF/THEN/ELSE/ENDIF
  • Connect a COBOL program's RETURN-CODE to its step's return code and the steps that follow
  • Write a procedure with symbolic parameters and call it with other values
  • Read COND tests as bypass tests and rewrite them with IF/THEN/ELSE without changing the job
  • Modify a procedure's statements from the calling job, and refer to GDG generations safely on a restart

Before you start

What you’ll learn · Before you start

What you will learn

  • Decide which steps of a job run from the return codes of earlier steps, with IF/THEN/ELSE/ENDIF
  • Connect a COBOL program's RETURN-CODE to its step's return code and the steps that follow
  • Write a procedure with symbolic parameters and call it with other values
  • Read COND tests as bypass tests and rewrite them with IF/THEN/ELSE without changing the job
  • Modify a procedure's statements from the calling job, and refer to GDG generations safely on a restart

Before you start

On this page

A nightly job of five steps must post only when validation ended with a return code of 4 or less, print totals only when nothing failed, and tell someone when anything ended with 8 or more. Two IF statements decide all three, and the same posting steps serve test and production through one procedure.

This lesson runs a chain of COBOL steps whose return codes decide what runs next, then writes the posting steps once as a procedure whose symbols change with each call.

A step’s return code decides which steps run next

conditional step: A job step that runs or is skipped by the return codes of the steps before it, under an IF/THEN/ELSE/ENDIF construct or a COND parameter. run or are skipped by the return codes of the steps before them. A COBOL program sets RETURN-CODE: A special register that a program sets to report how it ended. When the run ends, its value goes to the operating system as the exit status, which a shell shows in $?. More in “Install GnuCOBOL, compile with cobc and run programs”, and when it ends, that value becomes its step’s return code, which the job log shows as the condition code. The IF/THEN/ELSE/ENDIF statements of JCL then test it: IBM’s reference says RC means the highest return code of the steps that have run so far, and a step that did not start or ended abnormally is not counted. nightly.jcl uses two such tests:

nightly.jcl: concept listing, not run JCL · nightly.jcl
//NIGHTLY  JOB (ACCT),'NIGHTLY POSTING',CLASS=A,MSGCLASS=X
//* Concept listing, not run: an instream procedure with two
//* symbols, called for production, and steps run on return codes.
//POSTPROC PROC HLQ=BANK.TEST,RUNMODE=TRIAL
//POST     EXEC PGM=POSTTXN,PARM='MODE=&RUNMODE'
//STEPLIB  DD DISP=SHR,DSN=&HLQ..LOAD
//TXNIN    DD DISP=SHR,DSN=&HLQ..TXN.DAILY(0)
//POSTED   DD DSN=&HLQ..POSTED(+1),DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(115,12),RLSE),DCB=(RECFM=FB,LRECL=22)
//SYSOUT   DD SYSOUT=*
//         PEND
//EXTRACT  EXEC PGM=EXTRACT
//VALIDATE EXEC PGM=VALIDATE
//         IF (RC <= 4) THEN
//RUN      EXEC POSTPROC,HLQ=BANK.PROD,RUNMODE=FULL
//         ENDIF
//         IF (RC < 8) THEN
//REPORT   EXEC PGM=BRREPORT
//         ELSE
//NOTIFY   EXEC PGM=NOTIFY
//         ENDIF

Conditional steps can be tried on this computer, where an exit status plays the return code. stepend.cob stands in for each step’s program, and run_chain.sh applies the two IF statements, once for a night where POST ends with 0 and once for one where it ends with 12:

stepend.cob: stands in for each step's program COB · stepend.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. STEPEND.
      * Stands in for the program of one job step: it is given the
      * step's name and a return code, and ends with that code in
      * RETURN-CODE, as a real program does when it is done.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  COMMAND-TEXT            PIC X(40).
       01  STEP-NAME               PIC X(8).
       01  CODE-TEXT               PIC X(4).
       01  STEP-CODE               PIC 9(4).
       PROCEDURE DIVISION.
           ACCEPT COMMAND-TEXT FROM COMMAND-LINE
           UNSTRING COMMAND-TEXT DELIMITED BY ALL SPACE
               INTO STEP-NAME CODE-TEXT
           END-UNSTRING
           COMPUTE STEP-CODE = FUNCTION NUMVAL (CODE-TEXT)
           DISPLAY "  " STEP-NAME " runs and sets RETURN-CODE "
               STEP-CODE
           MOVE STEP-CODE TO RETURN-CODE
           GOBACK.
The five steps on a good night and a bad one Python · chain_session.py
from session import files, run

# Job NIGHTLY's five steps on this computer: stepend.cob stands in for each step's program, and run_chain.sh applies
# the IF/THEN/ELSE/ENDIF statements of nightly.jcl. First a night where POST ends with 0, then one where it ends with 12.
files("stepend.cob", "run_chain.sh")
run("""
cobc -x stepend.cob
bash run_chain.sh 0
bash run_chain.sh 12
""")

Output

$ cobc -x stepend.cob
$ bash run_chain.sh 0
  EXTRACT  runs and sets RETURN-CODE 0000
  VALIDATE runs and sets RETURN-CODE 0004
  POST     runs and sets RETURN-CODE 0000
  REPORT   runs and sets RETURN-CODE 0000
  NOTIFY is bypassed: the ELSE of IF (RC < 8)
highest return code of the job: 4
$ bash run_chain.sh 12
  EXTRACT  runs and sets RETURN-CODE 0000
  VALIDATE runs and sets RETURN-CODE 0004
  POST     runs and sets RETURN-CODE 0012
  REPORT is bypassed: RC 12 is not < 8
  NOTIFY   runs and sets RETURN-CODE 0000
highest return code of the job: 12

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 chain_session.py

On the good night RC was 4 after VALIDATE, so POST ran, and REPORT ran because RC stayed below 8. On the bad night POST ended with 12, so the ELSE branch ran NOTIFY in place of REPORT. Step through the bad night:

Interactive · stage by stage Job NIGHTLY on the night POST ends with 12 Step down the column. Each IF tests RC, the highest return code of the steps that have run so far.
Job NIGHTLY on the night POST ends with 12, step 5 of 5. Showing EXTRACT, VALIDATE, IF (RC <= 4) THEN, RUN.POST, IF (RC < 8) THEN, REPORT, NOTIFY. In focus: else, NOTIFY.ELSEEXTRACTends with RC 0VALIDATEends with RC 4IF (RC <= 4) THENRC is 4: trueRUN.POSTends with RC 12IF (RC < 8) THENRC is 12: falseREPORTbypassedNOTIFYthe ELSE: runs

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. Two steps run. EXTRACT ends with return code 0 and VALIDATE with 4, a warning. RC, the highest code so far, is 4.
  2. The first test. IF (RC <= 4) is true, so step RUN calls the procedure POSTPROC, and its step POST runs.
  3. POST fails. POSTTXN ends with return code 12, an error. RC is now 12, the highest code of the job.
  4. The second test. IF (RC < 8) is false. The steps between THEN and ELSE are bypassed, so REPORT does not print totals of a failed posting.
  5. The ELSE. The steps between ELSE and ENDIF run instead, so NOTIFY tells someone that the night failed. The job ends with RC 12 in its job log.

Older JCL decides the same things with the COND parameter of the EXEC statement, a bypass test: when one of its tests is true, the step is skipped. Where an IF says when the steps inside it run, a COND test says when its own step does not. A job that abends runs no later step by default, unless a test names the abend.

Checkpoint

Check: Return codes and IF/THEN/ELSE/ENDIF

  1. Question 1 of 2 Three steps have run with return codes 0, 8 and 4, in that order. What is RC in the next IF statement?

    Type a number.

    A hint

    RC is not the last code.

    Show the answer: Question 1 of 2

    Answer: 8

    IBM's reference says RC is the highest return code of the steps that ran before the IF is evaluated, so it is 8, not the 4 of the last step.

  2. Question 2 of 2 On the bad night, VALIDATE ended with 4 and POST with 12. Which statement of nightly.jcl decided that NOTIFY ran?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The ELSE of IF (RC < 8), because RC was 12

    IF (RC < 8) THEN REPORT ELSE NOTIFY ENDIF runs exactly one of the two: REPORT on a good night, NOTIFY when some step ended with 8 or more.

A procedure is a set of steps whose symbols each call can change

The posting step of nightly.jcl is written once, as a procedure, and called by a step with EXEC POSTPROC,HLQ=BANK.PROD,RUNMODE=FULL. procedure: A set of JCL steps written once and called by name from an EXEC statement: instream between PROC and PEND in the job, or cataloged as a member of a procedure library. come in two kinds: an instream procedure sits inside the job between a PROC and a PEND statement, and a cataloged procedure is a member of a procedure library that any job may call. The PROC statement gives each symbolic parameter: A name such as &HLQ in a JCL procedure that each call can give a value. The PROC statement sets its default, and a value on the calling EXEC statement replaces it for that call. a default, HLQ=BANK.TEST here, and a value on the calling EXEC statement wins over it. expand_proc.py expands POSTPROC both ways:

POSTPROC with its defaults, and as step RUN calls it Python · expand_proc.py
"""Expands the instream procedure POSTPROC of nightly.jcl the way the system does when a step calls it: the PROC
statement gives each symbol a default, the calling EXEC statement may give it another value, and every &name in the
procedure is replaced. A period right after a symbol is a delimiter and is dropped, so &HLQ..LOAD becomes
BANK.PROD.LOAD (z/OS MVS JCL Reference, Using symbols before fixed code). && starts a temporary data set name, not a
symbol."""
import re
from pathlib import Path

SYMBOL = re.compile(r"(?<!&)&([A-Z#@$][A-Z0-9#@$]{0,7})\.?")


def keywords(text):
    """HLQ=BANK.TEST,RUNMODE=TRIAL -> {'HLQ': 'BANK.TEST', 'RUNMODE': 'TRIAL'}"""
    return dict(part.split("=", 1) for part in text.split(",") if "=" in part)


def substitute(line, values):
    return SYMBOL.sub(lambda m: values.get(m.group(1), m.group(0)), line)


lines = Path("nightly.jcl").read_text().splitlines()
start = next(i for i, l in enumerate(lines) if " PROC " in l)
end = next(i for i, l in enumerate(lines) if l.split()[1:2] == ["PEND"])
defaults = keywords(lines[start].split(" PROC ", 1)[1].strip())
body = lines[start + 1:end]
call = next(l for l in lines[end:] if "EXEC POSTPROC" in l)
overrides = keywords(call.split("EXEC POSTPROC", 1)[1].strip(", "))

for title, values in (("called with the defaults", defaults), ("called by " + call.split()[0][2:], {**defaults, **overrides})):
    print(f"POSTPROC {title}: " + ", ".join(f"&{k}={v}" for k, v in values.items()))
    for line in body:
        if "&" in line:
            print("  " + substitute(line, values))

Output

POSTPROC called with the defaults: &HLQ=BANK.TEST, &RUNMODE=TRIAL
  //POST     EXEC PGM=POSTTXN,PARM='MODE=TRIAL'
  //STEPLIB  DD DISP=SHR,DSN=BANK.TEST.LOAD
  //TXNIN    DD DISP=SHR,DSN=BANK.TEST.TXN.DAILY(0)
  //POSTED   DD DSN=BANK.TEST.POSTED(+1),DISP=(NEW,CATLG,DELETE),
POSTPROC called by RUN: &HLQ=BANK.PROD, &RUNMODE=FULL
  //POST     EXEC PGM=POSTTXN,PARM='MODE=FULL'
  //STEPLIB  DD DISP=SHR,DSN=BANK.PROD.LOAD
  //TXNIN    DD DISP=SHR,DSN=BANK.PROD.TXN.DAILY(0)
  //POSTED   DD DSN=BANK.PROD.POSTED(+1),DISP=(NEW,CATLG,DELETE),

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 expand_proc.py

Called with the defaults, every data set is a BANK.TEST one and the PARM says TRIAL; called by step RUN, the same statements name BANK.PROD data sets and FULL. A period right after a symbol ends its name and disappears, which is why the procedure writes &HLQ..LOAD with two periods: IBM’s example is &DEPT..POK for D58.POK. Step through one statement:

Interactive · step by step One statement of POSTPROC, expanded for production Step through the expansion of the TXNIN DD statement. The procedure is written once; each call fills in its own values.
One statement of POSTPROC, expanded for production, step 4 of 4. Showing PROC, EXEC POSTPROC, TXNIN as coded, &HLQ for this call, TXNIN as run. In focus: TXNIN as run.PROCHLQ=BANK.TESTEXEC POSTPROCHLQ=BANK.PRODTXNIN as coded&HLQ..TXN.DAILY(0)&HLQ for this callBANK.PRODTXNIN as runBANK.PROD.TXN.DAILY(0)&HLQ. becomes BANK.PROD

The drawing shows the whole scene; its 4 steps are listed below.

Every step as text
  1. The procedure's default. The PROC statement gives the symbol HLQ the value BANK.TEST. A call that says nothing about HLQ runs the procedure against test data.
  2. The call's value. Step RUN calls the procedure with HLQ=BANK.PROD. A value on the calling EXEC statement wins over the PROC statement's default.
  3. The symbol in the statement. The DD statement is coded DSN=&HLQ..TXN.DAILY(0). The first period ends the symbol's name and disappears; the second stays in the data set name.
  4. The statement that runs. The system replaces &HLQ. with BANK.PROD, giving DSN=BANK.PROD.TXN.DAILY(0). The same procedure, called with the default, reads BANK.TEST.TXN.DAILY(0).

Three more statements help JCL procedures and symbols serve many jobs. JCLLIB names the private libraries searched for cataloged procedures and INCLUDE groups, one JCLLIB per job. INCLUDE brings a group of JCL statements from such a library into the job. SET gives symbols values for the statements that follow, and IBM’s reference says a value on a calling EXEC or PROC statement overrides the SET value.

Key idea

A step’s return code is its program’s RETURN-CODE, and IF tests the highest one so far; a procedure is written once, and its symbols take the values of each call.

Checkpoint

Check: Procedures and symbolic parameters

  1. Question 1 of 2 POSTPROC says PROC HLQ=BANK.TEST, and a step calls it with EXEC POSTPROC,HLQ=BANK.PROD. Which data set does its TXNIN DD read?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: BANK.PROD.TXN.DAILY(0)

    Defaults come from the PROC statement, and each call may give a symbol another value for that call only.

  2. Question 2 of 2 Why does POSTPROC write DSN=&HLQ..LOAD with two periods?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The first period ends the symbol and is dropped; the second stays in the data set name

    &HLQ..LOAD becomes BANK.PROD.LOAD: the symbol, its delimiting period, then .LOAD.

Practise: return codes, procedures and symbols

These questions use the two nights of chain_session.py and the expansion of POSTPROC.

Practice that adapts to you

Practise this lesson

6 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 In IF (RC <= 4) THEN, what is RC?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: The highest return code of the steps that have run so far

    Steps that did not start, were cancelled or abended are not counted in RC, and RC starts at 0.

  2. Question 2 of 6 How does a COBOL program decide the return code of its step?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: It moves the value to RETURN-CODE before it ends

    RETURN-CODE is a special register; GnuCOBOL passes it to the shell as the exit status, z/OS to the job as the step's return code.

  3. Question 3 of 6 STEP2 of a job abends. STEP3 has no IF around it and no COND. Does STEP3 run?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: No; by default no step runs after an abend

    An abend stops the normal flow. A clean-up step that must run anyway says so, with IF ABEND THEN or COND=EVEN.

  4. Question 4 of 6 Where does an instream procedure live?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: In the job itself, between a PROC and a PEND statement

    An instream procedure is handy for trying a procedure out; a cataloged one is shared by every job that needs it.

  5. Question 5 of 6 A test job calls EXEC POSTPROC and gives no symbols. What does step POST run with?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: BANK.TEST data sets and PARM='MODE=TRIAL'

    Defaults on the PROC statement make the safe values the ones a careless call gets: here test data and a trial run.

  6. Question 6 of 6 A job starts with SET HLQ=BANK.UAT and later calls EXEC POSTPROC,HLQ=BANK.PROD. Which value does the procedure use for HLQ?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: BANK.PROD, because a value on the calling EXEC statement overrides the SET value

    For a procedure, the call's value beats the PROC default and both beat SET, which carries into a procedure only when nothing else assigns the symbol.

COND, the bypass test, worked through every operator and three traps Pro: opens with a Pro, Premium or Ultimate pass

COND says when to skip a step, not when to run it, and the classic COND mistake is to read it the other way round. This part works through every operator, reproduces a COND that never fires, and drills the rules with sample tests.

In this part:

  • Reading COND the right way round, with IBM’s table
  • When COND cannot say what IF says
  • Abends, EVEN and ONLY, and the first step
  • Rewriting a COND job with IF, without changing what runs
  • A drill with sample tests
  • Decide whether COND bypasses a step
    • Results of the sample tests

2 runnable examples (JCL, Python) · 1 exercise with sample tests · about 7 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Overrides, symbols that eat periods, and GDG generations on a restart Pro: opens with a Pro, Premium or Ultimate pass

A procedure earns its keep when one call changes a little and the rest stays shared. This part overrides a cataloged procedure from a replay job, shows the period mistakes that make valid but wrong names, and restarts a job that had already created generations.

In this part:

  • Overriding a cataloged procedure from the calling job
  • The period after a symbol, and names that are valid and wrong
  • Restarting a job that had already created generations

1 diagram · 3 runnable examples (JCL, Python) · about 7 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

A warm-up answered in full, then three questions about conditions, procedures and restarts that misbehaved:

  1. Fresher to mid · Warm-up

    What is RC in a JCL IF statement, and how does a COBOL program set it?

    A COBOL program sets the return code of its step by moving a value to the special register RETURN-CODE before it ends, for example 0 for a clean run, 4 for warnings and 8 or more for errors. When the program ends, the value becomes the step's return code, the condition code in the job log. In IF (RC <= 4) THEN, RC is the highest return code of the steps that have run so far, not the last one; steps that did not start or ended abnormally are not counted. So after codes 0, 8 and 4, RC is 8. To test one step, name it, as in VALIDATE.RC. Locally, GnuCOBOL passes RETURN-CODE to the shell as the exit status.

  2. Mid to senior

    A team replaced IF (RC < 8) THEN REPORT ELSE NOTIFY ENDIF with COND parameters: COND=(8,LE) on REPORT and COND=(8,GT) on NOTIFY. Months later a night on which POST ended with 12 paged nobody. Explain why, and write a version that works.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A cataloged procedure moved from test to production. Its first production night ended with return code 0, but the branch summary was cataloged as BANK.PRODRPT instead of BANK.PROD.RPT, and the job that mails the summary found no data set. Find the cause, fix it, and stop it from happening again.

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A month-end job writes BANK.PROD.STMT(+1) in STEP1 and BANK.PROD.STMT(+2) in STEP2; STEP3 prints both and abended. The operator restarted at STEP3 without changing the JCL, and STEP3 failed again. What went wrong, and what are the two correct ways to recover?

    Model answer, Pro. First step and follow-ups

Organisations named on this page

The IF, COND, procedure, symbol and restart rules on this page follow IBM’s z/OS MVS JCL Reference and User’s Guide; the steps ran on this computer with GnuCOBOL 3.2. MySmartCoPilot has no link with IBM.

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Where does a JCL step's return code come from?

    From the COBOL program's RETURN-CODE when it ends; the job log shows it as the step's condition code.

  2. What does RC stand for in a JCL IF statement?

    The largest return code among the steps run so far; a step that never started or that abended does not count.

  3. How does the COND parameter differ from an IF statement?

    COND is a bypass test: when one of its tests is true, its own step is skipped. An IF says when the steps inside it run.

  4. Do later steps run after a step abends?

    Not by default: a job that abends runs no later step unless a test names the abend.

  5. How does an instream JCL procedure differ from a cataloged one?

    An instream procedure sits inside the job between PROC and PEND; a cataloged procedure is a member of a procedure library that any job may call.

  6. How does a symbolic parameter get its value?

    The PROC statement gives it a default, such as HLQ=BANK.TEST, and a value on the calling EXEC statement wins: EXEC POSTPROC,HLQ=BANK.PROD.

  7. Why does a procedure write &HLQ..LOAD with two periods?

    A period right after a symbol ends its name and vanishes, so the second period is the one that stays: BANK.PROD.LOAD.

  8. What do JCLLIB, INCLUDE and SET do?

    JCLLIB names the private libraries searched for procedures and INCLUDE groups; INCLUDE brings a group of JCL statements into the job; SET gives symbols values for the statements after it, and a value given on the calling EXEC or PROC statement wins over SET.

Key takeaways

  • A COBOL program’s RETURN-CODE becomes its step’s return code, and IF/THEN/ELSE/ENDIF tests RC, the highest code of the steps that have run, to decide which conditional steps run next.
  • A job that abends runs no later step by default; COND is the older way to write conditions, as a test that bypasses a step when it is true.
  • JCL procedures are written once, instream between PROC and PEND or cataloged in a library, and the symbols of each call replace their defaults; two periods keep one after a symbol.

Terms in this lesson

conditional step
A job step that runs or is skipped by the return codes of the steps before it, under an IF/THEN/ELSE/ENDIF construct or a COND parameter.
procedure
A set of JCL steps written once and called by name from an EXEC statement: instream between PROC and PEND in the job, or cataloged as a member of a procedure library.
symbolic parameter
A name such as &HLQ in a JCL procedure that each call can give a value. The PROC statement sets its default, and a value on the calling EXEC statement replaces it for that call.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.