JCL - 快速指南
JCL - Overview
何时使用JCL
JCL在大型机环境中用作程序(例如:COBOL,Assembler或PL/I)与操作系统之间的通信。 在大型机环境中,程序可以批量和在线模式执行。 批处理系统的示例可以是通过VSAM(虚拟存储访问方法)文件处理银行交易并将其应用于相应的帐户。 在线系统的示例可以是银行中的员工用来开立账户的后台屏幕。 在批处理模式下,程序通过JCL作为作业提交给操作系统。
批处理和在线处理在输入,输出和程序执行请求方面有所不同。 在批处理中,这些方面被馈送到JCL,而JCL又由操作系统接收。
工作处理
工作是一个工作单元,可以由许多工作步骤组成。 通过一组作业控制语句在作业控制语言(JCL)中指定每个作业步骤。
操作系统使用Job Entry System (JES)将作业接收到操作系统中,以安排它们进行处理和控制输出。
工作处理经历了以下给出的一系列步骤:
Job Submission -将JCL提交给JES。
Job Conversion - JCL与PROC一起转换为JES理解的解释文本并存储到数据集中,我们将其称为SPOOL。
Job Queuing - JES根据JOB语句中的CLASS和PRTY参数决定作业的优先级(在JCL - JOB Statement章节中进行了解释)。 检查JCL错误,如果没有错误,则将作业调度到作业队列中。
Job Execution -当作业达到其最高优先级时,将从作业队列中执行该作业。 从SPOOL读取JCL,执行程序并将输出重定向到JCL中指定的相应输出目标。
Purging -作业完成后,将释放分配的资源和JES SPOOL空间。 为了存储作业日志,我们需要在将作业日志从SPOOL释放之前将其复制到另一个数据集。
JCL - Environment Setup
在Windows/Linux上安装JCL
有许多可用于Windows的免费主机仿真器,可用于编写和学习示例JCL。
一个这样的模拟器是Hercules,它可以通过以下几个简单的步骤轻松安装在Windows中:
下载并安装Hercules仿真器,可从Hercules的主页获得: www.hercules-390.eu
在Windows计算机上安装软件包后,它将创建一个类似C:\Mainframes的文件夹。
运行命令提示符(CMD)并到达CMD上的目录C:\Mainframes。
有关编写和执行JCL的各种命令的完整指南,请访问URL www.jaymoseley.com/hercules/installmvs/instmvs2.htm
除了最新的64位z/Architecture之外,Hercules还是大型机System/370和ESA/390架构的开源软件实现。 Hercules在Linux,Windows,Solaris,FreeBSD和Mac OS X下运行。
在大型机上运行JCL
用户可以通过多种方式连接到大型机服务器,例如瘦客户端,虚拟终端,虚拟客户端系统(VCS)或虚拟桌面系统(VDS)。
每个有效用户都有一个登录ID进入Z/OS界面(TSO/E或ISPF)。 在Z/OS接口中,JCL可以编码并存储为分区数据集(PDS)中的成员。 提交JCL时,执行它并按照前一章的作业处理部分中的说明接收输出。
JCL的结构
具有共同语句的JCL的基本结构如下:
//SAMPJCL JOB 1,CLASS=6,MSGCLASS=0,NOTIFY=&SYSUID <b class="notranslate">(1)</b>
//* <b class="notranslate">(2)</b>
//STEP010 EXEC PGM=SORT <b class="notranslate">(3)</b>
//SORTIN DD DSN=JCL.SAMPLE.INPUT,DISP=SHR <b class="notranslate">(4)</b>
//SORTOUT DD DSN=JCL.SAMPLE.OUTPUT, <b class="notranslate">(5)</b>
// DISP=(NEW,CATLG,CATLG),DATACLAS=DSIZE50
//SYSOUT DD SYSOUT=* <b class="notranslate">(6)</b>
//SYSUDUMP DD SYSOUT=C <b class="notranslate">(6)</b>
//SYSPRINT DD SYSOUT=* <b class="notranslate">(6)</b>
//SYSIN DD * <b class="notranslate">(6)</b>
SORT FIELDS=COPY
INCLUDE COND=(28,3,CH,EQ,C'XXX')
/* <b class="notranslate">(7)</b>
程序说明
编号的JCL陈述已在下面解释:
(1) JOB statement - 指定(1) JOB statement的SPOOL所需的信息,例如作业ID,执行优先级,作业完成时通知的用户ID。
(2) //* statement - 这是一条评论声明。
(3) EXEC statement - 指定要执行的PROC /程序。 在上面的示例中,正在执行SORT程序(即,按特定顺序对输入数据进行排序)
(4) Input DD statement - 指定要传递给(3)中提到的程序的输入类型。 在上面的示例中,物理顺序(PS)文件在共享模式(DISP = SHR)中作为输入传递。
(5) Output DD statement - 指定程序在执行时产生的输出类型。 在上面的示例中,创建了一个PS文件。 如果一个语句超出了一行中的第70个位置,那么它将在下一行继续,该行应以“//”开头,后跟一个或多个空格。
(6)可以有其他类型的DD语句来为程序指定附加信息(在上面的示例中:SORT条件在SYSIN DD语句中指定)并指定错误/执行日志的目标(例如:SYSUDUMP/SYSPRINT)。 DD语句可以包含在数据集(大型机文件)中,也可以包含在流数据中(在JCL中硬编码的信息),如上例所示。
(7) /*标记流数据的结束。
除流数据外的所有JCL语句都以//开头。 在JOB,EXEC和DD关键字之前和之后应该至少有一个空格,并且该语句的其余部分不应该有任何空格。
JOB参数类型
每个JCL语句都附带一组参数,以帮助操作系统完成程序执行。 参数可以有两种类型:
位置参数
出现在语句中的预定义位置和顺序。 示例:会计信息参数只能在JOB关键字之后和程序员名称参数和关键字参数之前出现。 如果省略位置参数,则必须用逗号替换它。
位置参数存在于JOB和EXEC语句中。 在上面的例子中,PGM是在EXEC关键字之后编码的位置参数。
关键字参数
它们在位置参数之后编码,但可以按任何顺序出现。 如果不需要,可以省略关键字参数。 通用语法是KEYWORD = value 。 示例:MSGCLASS = X,即作业完成后作业日志重定向到输出SPOOL。
在上面的示例中,CLASS,MSGCLASS和NOTIFY是JOB语句的关键字参数。 EXEC语句中也可以有关键字参数。
这些参数已在后续章节中详细说明,并附有适当的示例。
JCL - JOB 语句
JOB语句是JCL中的第一个控制语句。 这会将作业的标识提供给操作系统(OS),假脱机和调度程序。 JOB语句中的参数可帮助操作系统分配正确的调度程序,所需的CPU时间并向用户发出通知。
语法 (Syntax)
以下是JCL JOB语句的基本语法:
//Job-name JOB Positional-param, Keyword-param
描述 (Description)
让我们看一下上面JOB语句语法中使用的术语的描述。
Job-name
这会在将作业提交给操作系统时为作业提供ID。 它可以是1到8的长度,带有字母数字字符,并在//之后开始。
JOB
这是将其标识为JOB语句的关键字。
Positional-param
有位置参数,可以有两种类型:
位置参数 | 描述 |
---|---|
Account information | 这是指CPU时间所欠的人或组。 它是根据拥有大型机的公司规则设置的。 如果将其指定为(*),则它将获取当前登录到大型机终端的用户的id。 |
Programmer name | 这标识了负责JCL的个人或团体。 这不是必需参数,可以用逗号替换。 |
Keyword-param
以下是各种关键字参数,可以在JOB语句中使用。 您可以根据需要使用一个或多个参数,并用逗号分隔:
关键字参数 | 描述 |
---|---|
CLASS | 根据作业所需的持续时间和资源数量,公司分配不同的工作类别。 这些可以被视为OS用于接收作业的各个调度程序。 将作业放在正确的调度程序中将有助于轻松执行作业。 有些公司在测试和生产环境中有不同的工作类别。 CLASS参数的有效值是A到Z字符和0到9数字(长度为1)。 以下是语法: CLASS=0 to 9 | A to Z |
PRTY | 指定作业类中作业的优先级。 如果未指定此参数,则作业将添加到指定CLASS中队列的末尾。 以下是语法: PRTY=N 其中N是0到15之间的数字,数字越高,优先级越高。 |
NOTIFY | 系统将成功或失败消息(最大条件代码)发送给此参数中指定的用户。 以下是语法: NOTIFY="userid | &SYSUID" 此处系统将消息发送给用户“userid”,但如果我们使用NOTIFY =&SYSUID,则将消息发送给提交JCL的用户。 |
MSGCLASS | 在作业完成时指定系统和作业消息的输出目标。 以下是语法: MSGCLASS=CLASS CLASS的有效值可以是“A”到“Z”和“0”到“9”。 可以将MSGCLASS = Y设置为将作业日志发送到JMR的类(JOBLOG管理和检索:大型机内的存储库以存储作业统计信息)。 |
MSGLEVEL | 指定要写入MSGCLASS中指定的输出目标的消息类型。 以下是语法: MSGLEVEL=( ST, MSG ) ST =写入输出日志的语句类型
MSG =写入输出日志的消息类型。
|
TYPRUN | 指定作业的特殊处理。 以下是语法: TYPRUN = SCAN | HOLD 其中SCAN和HOLD具有以下描述
|
TIME | 指定处理器用于执行作业的时间跨度。 以下是语法: TIME=(mm, ss) or TIME=ss 其中mm =分钟,ss =秒 在测试新编码的程序时,此参数非常有用。 为了确保程序由于循环错误而不能长时间运行,可以对时间参数进行编码,以便在达到指定的CPU时间时程序异常终止。 |
REGION | 指定在作业中运行作业步骤所需的地址空间。 以下是语法: REGION=nK | nM 这里, region可以指定为nK或nM,其中n是数字,K是千字节,M是兆字节。 当REGION = 0K或0M时,提供最大的地址空间用于执行。在关键应用中,禁止编码0K或0M以避免浪费地址空间。 |
例子 (Example)
//URMISAMP JOB (*),"tutpoint",CLASS=6,PRTY=10,NOTIFY=&SYSUID,
// MSGCLASS=X,MSGLEVEL=(1,1),TYPRUN=SCAN,
// TIME=(3,0),REGION=10K
在这里,JOB语句在一行中超出了第70个位置,因此我们继续在下一行中以“//”开头,后跟一个或多个空格。
杂项参数
其他参数很少,可以与JOB语句一起使用,但不经常使用:
ADDRSPC | 使用的存储类型:虚拟或真实 |
BYTES | 要写入输出日志的数据大小以及超出大小时要采取的操作。 |
LINES | 要输出日志的最大行数。 |
PAGES | 要输出日志的最大页数。 |
USER | 用于提交作业的用户ID |
PASSWORD | USER参数中指定的用户标识的密码。 |
COND and RESTART | 这些用于条件作业步骤处理,并在讨论条件处理时进行详细说明。 |
JCL - EXEC 语句
每个JCL都可以由许多工作步骤组成。 每个作业步骤可以直接执行程序或者可以调用程序,该程序又执行一个或多个程序(作业步骤)。 保存作业步骤程序/过程信息的EXEC statement.是EXEC statement.
EXEC语句的目的是为在作业步骤中执行的程序/过程提供所需的信息。 在此语句中编码的参数可以将数据传递给执行中的程序,可以覆盖JOB语句的某些参数,并且如果EXEC语句调用过程而不是直接执行程序,则可以将参数传递给过程。
语法 (Syntax)
以下是JCL EXEC语句的基本语法:
//Step-name EXEC Positional-param, Keyword-param
描述 (Description)
让我们看一下上面EXEC语句语法中使用的术语的描述。
STEP-NAME
这标识了JCL中的作业步骤。 它的长度可以是1到8,带有字母数字字符。
EXEC
这是将其标识为EXEC语句的关键字。
中方位-PARAM
这些是位置参数,可以有两种类型:
位置参数 | 描述 |
---|---|
PGM | 这是指要在作业步骤中执行的程序名称。 |
PROC | 这是指在作业步骤中要执行的过程名称。 我们将单独讨论它。 |
关键字PARAM
以下是EXEC语句的各种关键字参数。 您可以根据需要使用一个或多个参数,并用逗号分隔:
关键字参数 | 描述 |
---|---|
PARM | 用于向作业步骤中正在执行的程序提供参数化数据。 这是一个程序相关字段,没有明确的规则,除了在有特殊字符的情况下PARM值必须包含在引号内。 例如,下面给出了值“CUST1000”作为字母数字值传递给程序。 如果程序在COBOL中,则通过程序的LINKAGE SECTION接收通过JCL中的PARM参数传递的值。 |
ADDRSPC | 这用于指定作业步骤是否需要虚拟存储或实际存储来执行。 虚拟存储是可分页的,而实际存储不是并且被放置在主存储器中以供执行。 需要更快执行的作业步骤可以放在实际存储中。 以下是语法: ADDRSPC=VIRT | REAL 如果未对ADDRSPC进行编码,则VIRT是默认值。 |
ACCT | 这指定了作业步骤的会计信息。 以下是语法: ACCT=(userid) 这类似于JOB语句中的位置参数accounting information 。 如果它在JOB和EXEC语句中都被编码,则JOB语句中的记帐信息适用于未编码ACCT参数的所有作业步骤。 EXEC语句中的ACCT参数将仅覆盖该作业步骤的JOB语句中存在的参数。 |
EXEC和JOB语句的公共关键字参数
关键字参数 | 描述 |
---|---|
ADDRSPC | 在JOB语句中编码的ADDRSPC会覆盖在任何作业步骤的EXEC语句中编码的ADDRSPC。 |
TIME | 如果TIME在EXEC语句中编码,则它仅适用于该作业步骤。 如果在JOB和EXEC语句中都指定了它,则两者都将生效,并且可能由于其中任何一个而导致超时错误。 建议不要在JOB和EXEC语句中同时使用TIME参数。 |
REGION | 如果REGION在EXEC语句中编码,则它仅适用于该作业步骤。 在JOB语句中编码的REGION将覆盖在任何作业步骤的EXEC语句中编码的REGION。 |
COND | 用于根据上一步的返回代码控制作业步骤执行。 如果在作业步骤的EXEC语句中编码COND参数,则忽略JOB语句(如果存在)的COND参数。 在条件处理中解释了可以使用COND参数执行的各种测试。 |
例子 (Example)
以下是JCL脚本以及JOB和EXEC语句的简单示例:
//TTYYSAMP JOB 'TUTO',CLASS=6,MSGCLASS=X,REGION=8K,
// NOTIFY=&SYSUID
//*
//STEP010 EXEC PGM=MYCOBOL,PARAM=CUST1000,
// ACCT=(XXXX),REGION=8K,ADDRSPC=REAL,TIME=1440
JCL - DD 语句
数据集是具有以特定格式组织的记录的大型机文件。 数据集存储在直接访问存储设备(DASD)或大型机磁带上,是基本数据存储区域。 如果需要在批处理程序中使用/创建这些数据,则文件(即数据集)物理名称以及文件格式和组织将在JCL中编码。
使用DD statement给出JCL中使用的每个数据集的定义。 需要在DD语句中描述作业步骤所需的输入和输出资源,其中包含数据集组织,存储要求和记录长度等信息。
语法 (Syntax)
以下是JCL DD语句的基本语法:
//DD-name DD Parameters
描述 (Description)
让我们看一下上面DD语句语法中使用的术语的描述。
DD-NAME
DD-NAME标识数据集或输入/输出资源。 如果这是COBOL/Assembler程序使用的输入/输出文件,则该程序中的该名称将引用该文件。
DD
这是将其标识为DD语句的关键字。
参数
以下是DD语句的各种参数。 您可以根据需要使用一个或多个参数,并用逗号分隔:
参数 | 描述 |
---|---|
DSN | DSN参数引用新创建或现有数据集的物理数据集名称。 DSN值可以由1到8个字符长度的子名组成,以句点分隔,总长度为44个字符(字母数字)。 以下是语法: DSN=Physical Dataset Name Temporary datasets仅需要存储作业持续时间,并在作业完成时删除。 此类数据集表示为DSN=&name或仅指定DSN。 如果要在下一个作业步骤中使用作业步骤创建的临时数据集,则将其引用为DSN=*.stepname.ddname 。 这称为Backward Referencing. |
DISP | DISP参数用于描述数据集的状态,在正常和异常完成时作业步骤结束时的处置。 仅当在同一作业步骤(如临时数据集)中创建和删除数据集时,DD语句中才需要DISP。 以下是语法: DISP=(status, normal-disposition, abnormal-disposition) 以下是status有效值:
normal-disposition参数可以采用以下值之一
abnormal-disposition参数可以采用以下值之一
以下是CATLG,UNCATLG,DELETE,PASS和KEEP参数的说明:
如果未指定DISP的任何子参数,则默认值如下:
|
DCB | 数据控制块(DCB)参数详细说明了数据集的物理特征。 在作业步骤中新创建的数据集需要此参数。 LRECL是数据集中保存的每条记录的长度。 RECFM是数据集的记录格式。 RECFM可以保存值FB,V或VB。 FB是固定块组织,其中一个或多个逻辑记录在单个块中分组。 V是可变组织,其中一个可变长度逻辑记录放置在一个物理块内。 VB是可变块组织,其中一个或多个可变长度逻辑记录放置在一个物理块内。 BLKSIZE是物理块的大小。 块越大,FB或VB文件的记录数越大。 DSORG是数据集组织的类型。 DSORG可以保存值PS(物理顺序),PO(分区组织)和DA(直接组织)。 如果需要在同一个jobstep或JCL中将一个数据集的DCB值复制到另一个数据集,则将其指定为DCB = * .stepname.ddname,其中stepname是作业步骤的名称,ddname是数据集, DCB被复制。 检查下面的示例,其中RECFM = FB,LRECL = 80形成数据集OUTPUT1的DCB。 |
SPACE | SPACE参数指定DASD(直接访问存储磁盘)中数据集所需的空间。 以下是语法: SPACE=(spcunits, (pri, sec, dir), RLSE) 以下是所有使用参数的说明:
|
UNIT | UNIT和VOL参数列在编目数据集的系统目录中,因此只能使用物理DSN名称进行访问。 但对于未编目的数据集,DD语句应包含这些参数。 对于要创建的新数据集,可以指定UNIT/VOL参数,或者Z/OS分配合适的设备和卷。 UNIT参数指定存储数据集的设备类型。 可以使用硬件地址或设备类型组来识别设备类型。 以下是语法: UNIT=DASD | SYSDA 其中DASD代表直接访问存储设备,SYSDA代表系统直接访问,并指代下一个可用的磁盘存储设备。 |
VOL | VOL参数指定由UNIT参数标识的设备上的卷编号。 以下是语法: VOL=SER=(v1,v2) 其中v1,v2是卷序列号。 您也可以使用以下语法: VOL=REF=*.DDNAME 其中REF是JCL中任何前面作业步骤中数据集的卷序列号的后向引用。 |
SYSOUT | 到目前为止讨论的DD语句参数对应于存储在数据集中的数据。 SYSOUT参数根据指定的类将数据定向到输出设备。 以下是语法 SYSOUT=class 如果class为A,则它将输出定向到打印机,如果class为*则它将输出定向到JOB语句中MSGCLASS参数的目标。 |
例子 (Example)
以下是一个示例,它使用了DD语句以及上面解释的各种参数:
//TTYYSAMP JOB 'TUTO',CLASS=6,MSGCLASS=X,REGION=8K,
// NOTIFY=&SYSUID
//*
//STEP010 EXEC PGM=ICETOOL,ADDRSPC=REAL
//*
//INPUT1 DD DSN=TUTO.SORT.INPUT1,DISP=SHR
//INPUT2 DD DSN=TUTO.SORT.INPUT2,DISP=SHR,UNIT=SYSDA,
// VOL=SER=(1243,1244)
//OUTPUT1 DD DSN=MYFILES.SAMPLE.OUTPUT1,DISP=(,CATLG,DELETE),
// RECFM=FB,LRECL=80,SPACE=(CYL,(10,20))
//OUTPUT2 DD SYSOUT=*
JCL - Base Library
Base Library是分区数据集(PDS),它保存要在JCL中执行的程序的加载模块或在程序中调用的编目过程。 可以在JOBLIB库中为整个JCL指定基本库,也可以在STEPLIB语句中为特定作业步骤指定基本库。
JOBLIB Statement
JOBLIB语句用于标识要在JCL中执行的程序的位置。 JOBL语句在JOB语句之后和EXEC语句之前指定。 这只能用于流程序和程序。
语法 (Syntax)
以下是JCL JOBLIB语句的基本语法:
//JOBLIB DD DSN=dsnname,DISP=SHR
JOBLIB语句适用于JCL中的所有EXEC语句。 将在JOBLIB库中搜索EXEC语句中指定的程序,然后搜索系统库。
例如,如果EXEC语句正在执行COBOL程序,则COBOL程序的加载模块应放在JOBLIB库中。
STEPLIB Statement
使用STEPLIB语句来标识要在作业步骤中执行的程序的位置。 STEPLIB语句在EXEC语句之后和作业步骤的DD语句之前指定。
语法 (Syntax)
以下是JCL STEPLIB语句的基本语法:
//STEPLIB DD DSN=dsnname,DISP=SHR
将在STEPLIB库中搜索EXEC语句中指定的程序,然后搜索系统库。 在作业步骤中编码的STEPLIB会覆盖JOBLIB语句。
例子 (Example)
以下示例显示了JOBLIB和STEPLIB语句的用法:
//MYJCL JOB ,,CLASS=6,NOTIFY=&SYSUID
//*
//<b class="notranslate">JOBLIB</b> DD DSN=MYPROC.BASE.LIB1,DISP=SHR
//*
//STEP1 EXEC PGM=MYPROG1
//INPUT1 DD DSN=MYFILE.SAMPLE.INPUT1,DISP=SHR
//OUTPUT1 DD DSN=MYFILES.SAMPLE.OUTPUT1,DISP=(,CATLG,DELETE),
// RECFM=FB,LRECL=80
//*
//STEP2 EXEC PGM=MYPROG2
//<b class="notranslate">STEPLIB</b> DD DSN=MYPROC.BASE.LIB2,DISP=SHR
//INPUT2 DD DSN=MYFILE.SAMPLE.INPUT2,DISP=SHR
//OUTPUT2 DD DSN=MYFILES.SAMPLE.OUTPUT2,DISP=(,CATLG,DELETE),
// RECFM=FB,LRECL=80
这里,在MYPROC.SAMPLE.LIB1中搜索程序MYPROG1的加载模块(在STEP1中)。 如果未找到,则在系统库中搜索。 在STEP2中,STEPLIB覆盖JOBLIB,并在MYPROC.SAMPLE.LIB2中搜索程序MYPROG2的加载模块,然后在系统库中搜索。
INCLUDE Statement
可以使用INCLUDE语句将在PDS成员中编码的一组JCL语句包括在JCL中。 当JES解释JCL时,INCLUDE成员中的一组JCL语句将替换INCLUDE语句。
语法 (Syntax)
以下是JCL INCLUDE语句的基本语法:
//name <b class="notranslate">INCLUDE</b> MEMBER=member-name
INCLUDE语句的主要目的是可重用性。 例如,跨多个JCL使用的公共文件可以编码为INCLUDE成员中的DD语句,并在JCL中使用。
Dummy DD语句,数据卡规范,PROC,JOB,PROC语句不能在INCLUDE成员中编码。 INLCUDE语句可以在INCLUDE成员中编码,并且可以进一步嵌套最多15个级别。
JCLLIB Statement
JCLLIB语句用于标识作业中使用的私有库。 它既可以用于instream也可以用于编目程序。
语法 (Syntax)
以下是JCL JCLLIB语句的基本语法:
//name <b class="notranslate">JCLLIB</b> ORDER=(library1, library2....)
将按给定顺序搜索JCLLIB语句中指定的库,以查找作业中使用的程序,过程和INCLUDE成员。 JCL中只能有一个JCLLIB语句; 在JOB语句之后和EXEC和INCLUDE语句之前指定,但它不能在INCLUDE成员中编码。
例子 (Example)
在以下示例中,按MYPROC.BASE.LIB1,MYPROC.BASE.LIB2,系统库的顺序搜索程序MYPROG3和INCLUDE成员MYINCL。
//MYJCL JOB ,,CLASS=6,NOTIFY=&SYSUID
//*
//MYLIB <b class="notranslate">JCLLIB</b> ORDER=(MYPROC.BASE.LIB1,MYPROC.BASE.LIB2)
//*
//STEP1 EXEC PGM=<b class="notranslate">MYPROG3</b>
//INC <b class="notranslate">INCLUDE</b> MEMBER=MYINCL
//OUTPUT1 DD DSN=MYFILES.SAMPLE.OUTPUT1,DISP=(,CATLG,DELETE),
// RECFM=FB,LRECL=80
//*
JCL - Procedures
JCL Procedures是JCL Procedures内部的一组语句,它们组合在一起以执行特定的功能。 通常,JCL的固定部分在程序中编码。 Job的变化部分在JCL中编码。
您可以使用过程来使用多个输入文件来实现程序的并行执行。 可以为每个输入文件创建JCL,并且可以通过将输入文件名作为符号参数传递来同时调用单个过程。
语法 (Syntax)
以下是JCL过程定义的基本语法:
//*
//<i>Step-name</i> <b class="notranslate">EXEC</b> <i>procedure name</i>
该过程的内容保存在JCL内,用于流程过程。 内容保存在基本库的不同成员中,用于编目过程。 本章将解释JCL中可用的两种类型的过程,最后我们将看到如何嵌套各种过程。
流程程序
当过程在同一个JCL成员中编码时,它被称为“插入过程”。 它应该以PROC语句开头,并以PEND语句结束。
//SAMPINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//INSTPROC <b class="notranslate">PROC</b> //*START OF PROCEDURE
//PROC1 EXEC PGM=SORT
//SORTIN DD DSN=&DSNAME,DISP=SHR
//SORTOUT DD SYSOUT=*MYINCL
//SYSOUT DD SYSOUT=*
//SYSIN DD DSN=&DATAC LRECL=80
// <b class="notranslate">PEND</b> //*END OF PROCEDURE
//*
//STEP1 EXEC INSTPROC,DSNME=MYDATA.URMI.INPUT1,
// DATAC=MYDATA.BASE.LIB1(DATA1)
//*
//STEP2 EXEC INSTPROC,DSNME=MYDATA.URMI.INPUT2
// DATAC=MYDATA.BASE.LIB1(DATA1)
//*
在上面的示例中,使用不同的输入文件在STEP1和STEP2中调用过程INSTPROC。 在调用过程时,参数DSNAME和DATAC可以用不同的值编码,这些symbolic parameters被称为symbolic parameters 。 JCL的变化输入(例如文件名,数据卡,PARM值等)作为符号参数传递给过程。
编码符号参数时,请勿使用KEYWORDS,PARAMETERS或SUB-PARAMETERS作为符号名称。 示例:不要使用TIME =&TIME但是可以使用TIME =&TM,它被认为是编码符号的正确方法。
用户定义的符号参数称为JCL Symbols 。 有一些称为system symbols ,用于登录作业执行。 普通用户在批处理作业中使用的唯一系统符号是&SYSUID ,这在JOB语句的NOTIFY参数中使用。
编目程序
当过程从JCL中分离出来并在不同的数据存储中编码时,它称为“已Cataloged Procedure 。 PROC语句不是必须在编目过程中编码。 以下是JCL的示例,它调用CATLPROC过程:
//SAMPINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//STEP EXEC <b class="notranslate">CATLPROC</b>,PROG=CATPRC1,DSNME=MYDATA.URMI.INPUT
// DATAC=MYDATA.BASE.LIB1(DATA1)
这里,程序CATLPROC在MYCOBOL.BASE.LIB1中编目。 PROG,DATAC和DSNAME作为符号参数传递给过程CATLPROC。
//CATLPROC <b class="notranslate">PROC</b> PROG=,BASELB=MYCOBOL.BASE.LIB1
//*
//PROC1 EXEC PGM=&PROG
//STEPLIB DD DSN=&BASELB,DISP=SHR
//IN1 DD DSN=&DSNAME,DISP=SHR
//OUT1 DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD DSN=&DATAC
//*
在该过程中,符号参数PROG和BASELB被编码。 请注意,过程中的PROG参数被JCL中的值覆盖,因此PGM在执行期间取值CATPRC1。
嵌套程序
从过程中调用过程称为nested procedure 。 程序最多可嵌套15个级别。 嵌套可以完全插入或编目。 我们无法在编目过程中编写一个instream过程。
//SAMPINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//SETNM SET DSNM1=INPUT1,DSNM2=OUTPUT1
//INSTPRC1 <b class="notranslate">PROC</b> //* START OF PROCEDURE 1
//STEP1 EXEC PGM=SORT,DISP=SHR
//SORTIN DD DSN=&DSNM1,DISP=SHR
//SORTOUT DD DSN=&DSNM2,DISP=(,PASS)
//SYSOUT DD SYSOUT=*
//SYSIN DD DSN=&DATAC
//*
//STEP2 EXEC <b class="notranslate">PROC=INSTPRC2</b>,DSNM2=MYDATA.URMI.OUTPUT2
// <b class="notranslate">PEND</b> //* END OF PROCEDURE 1
//*
//INSTPRC2 <b class="notranslate">PROC</b> //* START OF PROCEDURE 2
//STEP1 EXEC PGM=SORT
//SORTIN DD DSN=*.INSTPRC1.STEP1.SORTOUT
//SORTOUT DD DSN=&DSNM2,DISP=OLD
//SYSOUT DD SYSOUT=*
//SYSIN DD DSN=&DATAC
// <b class="notranslate">PEND</b> //* END OF PROCEDURE 2
//*
//JSTEP1 EXEC INSTPRC1,DSNM1=MYDATA.URMI.INPUT1,
// DATAC=MYDATA.BASE.LIB1(DATA1)
//*
在上面的示例中,JCL在JSTEP1中调用过程INSTPRC1,并在过程INSTPRC1中调用过程INSTPRC2。 这里,INSTPRC1(SORTOUT)的输出作为输入(SORTIN)传递给INSTPRC2。
SET statement用于定义作业步骤或过程中常用的符号。 它初始化符号名称中的先前值。 它必须在JCL中第一次使用符号名称之前定义。
让我们看一下下面的描述,以便更多地了解上述程序:
SET参数初始化DSNM1 = INPUT1和DSNM2 = OUTPUT1。
当在JCL的JSTEP1中调用INSTPRC1时,DSNM1 = MYDATA.URMI.INPUT1和DSNM2 = OUTPUT1。,即,在SET语句中初始化的值将使用在任何作业步骤/过程中设置的值重置。
在INSTPRC1的STEP2中调用INSTPRC2时,DSNM1 = MYDATA.URMI.INPUT1和DSNM2 = MYDATA.URMI.OUTPUT2。
JCL - Conditional Processing
作业输入系统使用两种方法在JCL中执行条件处理。 作业完成后,将根据执行状态设置返回代码。 返回码可以是0(成功执行)到4095之间的数字(非零表示错误条件)。 最常见的传统价值观是:
0 =正常 - 一切正常
4 =警告 - 轻微错误或问题。
8 =错误 - 重大错误或问题。
12 =严重错误 - 重大错误或问题,结果不值得信任。
16 =终端错误 - 非常严重的问题,不要使用结果。
可以使用COND参数和IF-THEN-ELSE结构,基于前一步骤的返回代码来控制作业步骤执行,本教程已对此进行了解释。
COND参数
COND参数可以在JCL的JOB或EXEC语句中编码。 它是对前面作业步骤的返回码的测试。 如果评估测试为真,则绕过当前作业步骤执行。 绕过只是省略了工作步骤而不是异常终止。 在一次测试中最多可以组合八种条件。
语法 (Syntax)
以下是JCL COND参数的基本语法:
COND=(rc,logical-operator)
or
COND=(rc,logical-operator,stepname)
or
COND=EVEN
or
COND=ONLY
以下是所用参数的说明:
rc :这是返回码
logical-operator :可以是GT(大于),GE(大于或等于),EQ(等于),LT(小于),LE(小于或等于)或NE(不等于)。
stepname :这是作业步骤,其返回代码用于测试。
最后两个条件(a)COND = EVEN和(b)COND = ONLY,已在本教程的下面解释。
COND可以在JOB语句或EXEC语句中编码,在这两种情况下,它的行为都不同,如下所述:
COND inside JOB 语句
当在JOB语句中编码COND时,将针对每个作业步骤测试条件。 当条件在任何特定作业步骤为真时,它将与其后的作业步骤一起被绕过。 以下是一个例子:
//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID,COND=(5,LE)
//*
//STEP10 EXEC PGM=FIRSTP
<b class="notranslate">//* STEP10 executes without any test being performed.</b>
//STEP20 EXEC PGM=SECONDP
<b class="notranslate">//* STEP20 is bypassed, if RC of STEP10 is 5 or above.
//* Say STEP10 ends with RC4 and hence test is false.
//* So STEP20 executes and lets say it ends with RC16.</b>
//STEP30 EXEC PGM=SORT
<b class="notranslate">//* STEP30 is bypassed since 5 <= 16.</b>
COND inside EXEC 语句
当COND在作业步骤的EXEC语句中编码并被发现为真时,仅绕过该作业步骤,并从下一个作业步骤继续执行。
//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=SORT
<b class="notranslate">//* Assuming STP01 ends with RC0.</b>
//STP02 EXEC PGM=MYCOBB,COND=(0,EQ,STP01)
<b class="notranslate">//* In STP02, condition evaluates to TRUE and step bypassed.</b>
//STP03 EXEC PGM=IEBGENER,COND=((10,LT,STP01),(10,GT,STP02))
<b class="notranslate">//* In STP03, first condition fails and hence STP03 executes.
//* Since STP02 is bypassed, the condition (10,GT,STP02) in
//* STP03 is not tested.</b>
COND=EVEN
当编码COND = EVEN时,即使前面的任何步骤异常终止,也执行当前作业步骤。 如果任何其他RC条件与COND = EVEN一起编码,则如果RC条件均不为真,则执行作业步骤。
//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=SORT
<b class="notranslate">//* Assuming STP01 ends with RC0.</b>
//STP02 EXEC PGM=MYCOBB,COND=(0,EQ,STP01)
<b class="notranslate">//* In STP02, condition evaluates to TRUE and step bypassed.</b>
//STP03 EXEC PGM=IEBGENER,COND=((10,LT,STP01),EVEN)
<b class="notranslate">//* In STP03, condition (10,LT,STP01) evaluates to true,
//* hence the step is bypassed.</b>
COND=ONLY
当编码COND = ONLY时,仅当前面的任何步骤异常终止时才执行当前作业步骤。 如果任何其他RC条件与COND = ONLY一起编码,则如果RC条件均不为真且任何先前作业步骤异常失败,则执行作业步骤。
//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=SORT
<b class="notranslate">//* Assuming STP01 ends with RC0.</b>
//STP02 EXEC PGM=MYCOBB,COND=(4,EQ,STP01)
<b class="notranslate">//* In STP02, condition evaluates to FALSE, step is executed
//* and assume the step abends.</b>
//STP03 EXEC PGM=IEBGENER,COND=((0,EQ,STP01),ONLY)
<b class="notranslate">//* In STP03, though the STP02 abends, the condition
//* (0,EQ,STP01) is met. Hence STP03 is bypassed.</b>
IF-THEN-ELSE Construct
控制作业处理的另一种方法是使用IF-THEN-ELSE结构。 这为条件处理提供了更多的灵活性和用户友好的方式。
语法 (Syntax)
以下是JCL IF-THEN-ELSE构造的基本语法:
//name IF condition THEN
list of statements //* action to be taken when condition is true
//name ELSE
list of statements //* action to be taken when condition is false
//name ENDIF
以下是上述IF-THEN-ELSE构造中使用的术语的描述:
name :这是可选的,名称可以包含1到8个以字母,#,$或@开头的字母数字字符。
Condition :条件将具有以下格式: KEYWORD OPERATOR VALUE ,其中KEYWORDS可以是RC(返回代码),ABENDCC(系统或用户完成代码),ABEND,RUN(步骤开始执行)。 OPERATOR可以是逻辑运算符(AND(&),OR(|))或关系运算符(“,”=,“,”=,“”)。
例子 (Example)
以下是一个显示IF-THEN-ELSE用法的简单示例:
//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//PRC1 PROC
//PST1 EXEC PGM=SORT
//PST2 EXEC PGM=IEBGENER
// PEND
//STP01 EXEC PGM=SORT
//IF1 IF STP01.RC = 0 THEN
//STP02 EXEC PGM=MYCOBB1,PARM=123
// ENDIF
//IF2 IF STP01.RUN THEN
//STP03a EXEC PGM=IEBGENER
//STP03b EXEC PGM=SORT
// ENDIF
//IF3 IF STP03b.!ABEND THEN
//STP04 EXEC PGM=MYCOBB1,PARM=456
// ELSE
// ENDIF
//IF4 IF (STP01.RC = 0 & STP02.RC <= 4) THEN
//STP05 EXEC PROC=PRC1
// ENDIF
//IF5 IF STP05.PRC1.PST1.ABEND THEN
//STP06 EXEC PGM=MYABD
// ELSE
//STP07 EXEC PGM=SORT
// ENDIF
让我们试着看一下上面的程序,以便更详细地理解它:
在IF1中测试STP01的返回码。 如果为0,则执行STP02。 否则,处理进入下一个IF语句(IF2)。
在IF2中,如果STP01已经开始执行,则执行STP03a和STP03b。
在IF3中,如果STP03b没有ABEND,则执行STP04。 在ELSE中,没有任何陈述。 它被称为NULL ELSE语句。
在IF4中,如果STP01.RC = 0且STP02.RC <= 4为TRUE,则执行STP05。
在IF5中,如果PROC PRC1中的proc-step PST1在jobstep STP05 ABEND中执行,则执行STP06。 其他STP07已执行。
如果IF4的计算结果为false,则不执行STP05。 在这种情况下,不测试IF5并且不执行步骤STP06,STP07。
如果作业异常终止(例如,用户取消作业,作业时间到期或数据集后向参考被绕过的步骤),则不会执行IF-THEN-ELSE。
设置检查点
您可以使用SYSCKEOV,这是一个DD语句)在JCL程序中设置检查点数据集。
CHKPT是在DD语句中为多卷QSAM数据集编码的参数。 当CHKPT编码为CHKPT = EOV时,检查点将写入输入/输出多卷数据集的每个卷末尾的SYSCKEOV语句中指定的数据集。
//CHKSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=MYCOBB
//SYSCKEOV DD DSNAME=SAMPLE.CHK,DISP=MOD
//IN1 DD DSN=SAMPLE.IN,DISP=SHR
//OUT1 DD DSN=SAMPLE.OUT,DISP=(,CATLG,CATLG)
// CHKPT=EOV,LRECL=80,RECFM=FB
在上面的示例中,检查点写在输出数据集SAMPLE.OUT的每个卷末尾的数据集SAMPLE.CHK中。
重启处理
您可以使用RD parameter或手动使用RESTART parameter以自动方式重新启动处理以太网。
RD parameter在JOB或EXEC语句中编码,它有助于自动JOB/STEP重启,并且可以保存四个值之一:R,RNC,NR或NC。
RD=R允许自动重启并考虑在DD语句的CHKPT参数中编码的检查点。
RD=RNC允许自动重启,但覆盖(忽略)CHKPT参数。
RD=NR指定无法自动重新启动作业/步骤。 但是当使用RESTART参数手动重新启动时,将考虑CHKPT参数(如果有)。
RD=NC不允许自动重启和检查点处理。
如果仅要求对特定异常SCHEDxx代码执行自动重新启动,则可以在IBM系统parmlib库的SCHEDxx成员中指定它。
RESTART parameter在JOB或EXEC语句中编码,有助于在作业失败后手动重启JOB/STEP。 RESTART可以附带一个checkid,它是在SYSCKEOV DD语句中编码的数据集中编写的检查点。 编码checkid时,SYSCHK DD语句应编码为在JOBLIB语句之后引用检查点数据集(如果有),否则在JOB语句之后引用。
//CHKSAMP JOB CLASS=6,NOTIFY=&SYSUID,RESTART=(STP01,chk5)
//*
//SYSCHK DD DSN=SAMPLE.CHK,DISP=OLD
//STP01 EXEC PGM=MYCOBB
//*SYSCKEOV DD DSNAME=SAMPLE.CHK,DISP=MOD
//IN1 DD DSN=SAMPLE.IN,DISP=SHR
//OUT1 DD DSN=SAMPLE.OUT,DISP=(,CATLG,CATLG)
// CHKPT=EOV,LRECL=80,RECFM=FB
在上面的例子中,chk5是checkid,即在checkpoint5重新启动STP01。 请注意,在设置检查点部分中解释的先前程序中添加了SYSCHK语句并注释掉了SYSCKEOV语句。
JCL - Defining Datasets
数据集名称指定文件的名称,它在JCL中由DSN表示。 DSN参数引用新创建或现有数据集的物理数据集名称。 DSN值可以由1到8个字符长度的子名组成,以句点分隔,总长度为44个字符(字母数字)。 以下是语法:
<b class="notranslate">DSN=&name | *.stepname.ddname</b>
Temporary datasets仅需要存储作业持续时间,并在作业完成时删除。 此类数据集表示为DSN=&name或仅指定DSN。
如果要在下一个作业步骤中使用作业步骤创建的临时数据集,则将其引用为DSN=*.stepname.ddname 。 这称为Backward Referencing.
连接数据集
如果存在多个相同格式的数据集,则可以将它们连接起来并作为输入传递给单个DD名称中的程序。
//CONCATEX JOB CLASS=6,NOTIFY=&SYSUID
//*
//STEP10 EXEC PGM=SORT
//SORTIN DD DSN=SAMPLE.INPUT1,DISP=SHR
// DD DSN=SAMPLE.INPUT2,DISP=SHR
// DD DSN=SAMPLE.INPUT3,DISP=SHR
//SORTOUT DD DSN=SAMPLE.OUTPUT,DISP=(,CATLG,DELETE),
// LRECL=50,RECFM=FB
在上面的示例中,三个数据集连接在一起,并作为输入传递给SORTIN DD名称中的SORT程序。 合并文件,在指定的键字段上排序,然后写入SORTOUT DD名称中的单个输出文件SAMPLE.OUTPUT。
覆盖数据集
在标准化的JCL中,要执行的程序及其相关数据集放在编目过程中,该过程在JCL中调用。 通常,出于测试目的或事件修复,可能需要使用除编目过程中指定的数据集之外的其他数据集。 在这种情况下,可以在JCL中覆盖过程中的数据集。
//SAMPINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//JSTEP1 EXEC <b class="notranslate">CATLPROC</b>,PROG=CATPRC1,DSNME=MYDATA.URMI.INPUT
// DATAC=MYDATA.BASE.LIB1(DATA1)
//STEP1.IN1 DD DSN=MYDATA.OVER.INPUT,DISP=SHR
//*
//* The cataloged procedure is as below:
//*
//CATLPROC <b class="notranslate">PROC</b> PROG=,BASELB=MYCOBOL.BASE.LIB1
//*
//STEP1 EXEC PGM=&PROG
//STEPLIB DD DSN=&BASELB,DISP=SHR
//IN1 DD DSN=MYDATA.URMI.INPUT,DISP=SHR
//OUT1 DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD MYDATA.BASE.LIB1(DATA1),DISP=SHR
//*
//STEP2 EXEC PGM=SORT
在上面的示例中,数据集IN1使用PROC中的文件MYDATA.URMI.INPUT,该文件在JCL中被覆盖。 因此,执行中使用的输入文件是MYDATA.OVER.INPUT。 请注意,数据集称为STEP1.IN1。 如果JCL/PROC中只有一个步骤,则只能使用DD名称引用数据集。 同样,如果JCL中有多个步骤,则数据集将被重写为JSTEP1.STEP1.IN1。
//SAMPINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//STEP EXEC <b class="notranslate">CATLPROC</b>,PROG=CATPRC1,DSNME=MYDATA.URMI.INPUT
// DATAC=MYDATA.BASE.LIB1(DATA1)
//STEP1.IN1 DD DSN=MYDATA.OVER.INPUT,DISP=SHR
// DD DUMMY
// DD DUMMY
//*
在上面的示例中,在IN1中连接的三个数据集中,第一个在JCL中被重写,其余数据保存在PROC中。
在JCL中定义GDG
生成数据组(GDG)是通过通用名称彼此相关的数据集组。 通用名称称为GDG基础,与基础关联的每个数据集称为GDG版本。
例如,MYDATA.URMI.SAMPLE.GDG是GDG基本名称。 数据集命名为MYDATA.URMI.SAMPLE.GDG.G0001V00,MYDATA.URMI.SAMPLE.GDG.G0002V00等。 最新版本的GDG称为MYDATA.URMI.SAMPLE.GDG(0),以前的版本称为(-1),( - 2),依此类推。 在程序中创建的下一个版本在JCL中称为MYDATA.URMI.SAMPLE.GDG(+1)。
在JCL中创建/更改GDG
GDG版本可以具有相同或不同的DCB参数。 初始模型DCB可以定义为由所有版本使用,但在创建新版本时可以覆盖它。
//GDGSTEP1 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DEFINE GDG(NAME(MYDATA.URMI.SAMPLE.GDG) -
LIMIT(7) -
NOEMPTY -
SCRATCH)
/*
//GDGSTEP2 EXEC PGM=IEFBR14
//GDGMODLD DD DSN=MYDATA.URMI.SAMPLE.GDG,
// DISP=(NEW,CATLG,DELETE),
// UNIT=SYSDA,
// SPACE=(CYL,10,20),
// DCB=(LRECL=50,RECFM=FB)
//
在上面的示例中,IDCAMS实用程序使用SYSIN DD语句中传递的以下参数在GDGSTEP1中定义GDG基础:
NAME指定GDG基础的物理数据集名称。
LIMIT指定GDG基础可容纳的最大版本数。
当达到LIMIT时, EMPTY取消所有代的编目。
NOEMPTY取消了最近一代的编目。
SCRATCH在未编目时物理删除生成。
NOSCRATCH不删除数据集,即可以使用UNIT和VOL参数引用它。
在GDGSTEP2中,IEFBR14实用程序指定所有版本使用的模型DD参数。
IDCAMS可用于更改GDG的定义参数,例如增加LIMIT,将EMPTY更改为NOEMPTY等,使用SYSIN命令的相关版本为ALTER MYDATA.URMI.SAMPLE.GDG LIMIT(15) EMPTY 。
在JCL中删除GDG
使用IEFBR14实用程序,我们可以删除单个版本的GDG。
//GDGSTEP3 EXEC PGM=IEFBR14
//GDGDEL DD DSN=MYDATA.URMI.SAMPLE.GDG(0),
// DISP=(OLD,DELETE,DELETE)
在上面的示例中,删除了最新版本的MYDATA.URMI.SAMPLE.GDG。 请注意,正常作业完成时的DISP参数编码为DELETE。 因此,当作业完成执行时,将删除数据集。
IDCAMS可用于使用SYSIN命令DELETE(MYDATA.URMI.SAMPLE.GDG) GDG FORCE/PURGE删除GDG及其相关版本。
FORCE删除GDG版本和GDG基础。 如果任何GDG版本设置的过期日期尚未到期,则不会删除这些版本,因此将保留GDG基础。
无论过期日期如何, PURGE删除GDG版本和GDG基础。
在JCL中使用GDG
在以下示例中,最新版本的MYDATA.URMI.SAMPLE.GDG用作程序的输入,并创建新版本的MYDATA.URMI.SAMPLE.GDG作为输出。
//CNDSAMP JOB CLASS=6,NOTIFY=&SYSUID
//*
//STP01 EXEC PGM=MYCOBB
//IN1 DD DSN=MYDATA.URMI.SAMPLE.GDG(0),DISP=SHR
//OUT1 DD DSN=MYDATA.URMI.SAMPLE.GDG(+1),DISP=(,CALTG,DELETE)
// LRECL=100,RECFM=FB
在这里,如果GDG已被实际名称引用,如MYDATA.URMI.SAMPLE.GDG.G0001V00,那么每次执行前它都会导致更改JCL。 使用(0)和(+1)使其动态替代GDG版本执行。
Input-Output Methods
通过JCL执行的任何批处理程序都需要数据输入,处理并创建输出。 有不同的方法向程序输入输入并写入从JCL接收的输出。 在批处理模式下,不需要用户交互,但在JCL中定义输入和输出设备以及所需的组织并提交。
JCL中的数据输入
有多种方法可以使用JCL将数据提供给程序,这些方法已在下面说明:
INSTREAM数据
可以使用SYSIN DD语句指定程序的Instream数据。
//CONCATEX JOB CLASS=6,NOTIFY=&SYSUID
//* <b class="notranslate">Example 1:</b>
//STEP10 EXEC PGM=MYPROG
//IN1 DD DSN=SAMPLE.INPUT1,DISP=SHR
//OUT1 DD DSN=SAMPLE.OUTPUT1,DISP=(,CATLG,DELETE),
// LRECL=50,RECFM=FB
//SYSIN DD *
//CUST1 1000
//CUST2 1001
/*
//*
//* <b class="notranslate">Example 2:</b>
//STEP20 EXEC PGM=MYPROG
//OUT1 DD DSN=SAMPLE.OUTPUT2,DISP=(,CATLG,DELETE),
// LRECL=50,RECFM=FB
//SYSIN DD DSN=SAMPLE.SYSIN.DATA,DISP=SHR
//*
在示例1中,MYPROG的输入通过SYSIN传递。 数据在JCL中提供。 两个数据记录传递给该程序。 请注意,/ *标记了异常SYSIN数据的结束。
“CUST1 1000”是record1,“CUST2 1001”是record2。 在读取数据时遇到符号/ *时,满足数据结束条件。
在示例2中,SYSIN数据保存在数据集中,其中SAMPLE.SYSIN.DATA是PS文件,可以保存一个或多个数据记录。
数据通过文件输入
如前面章节中的大多数示例所述,可以通过PS,VSAM或GDG文件提供输入到程序的数据,以及相关的DSN名称和DISP参数以及DD语句。
在示例1中,SAMPLE.INPUT1是输入文件,通过该文件将数据传递给MYPROG。 它在程序中称为IN1。
JCL中的数据输出
JCL中的输出可以编目到数据集中或传递给SYSOUT。 如DD语句章节中所述, SYSOUT=*将输出重定向到与JOB语句的MSGCLASS参数中提到的类相同的类。
保存作业日志
指定MSGCLASS=Y会将作业日志保存在JMR(作业日志管理和检索)中。 整个JOB日志可以重定向到SPOOL,并可以通过针对SPOOL中的作业名称提供XDC命令来保存到数据集。 在SPOOL中给出XDC命令时,将打开数据集创建屏幕。 然后,可以通过提供适当的PS或PDS定义来保存作业日志。
通过提及已创建的SYSOUT和SYSPRINT数据集,还可以将作业日志保存到数据集中。 但是,无法通过JMR或XDC中的方式捕获整个作业日志(即,JESMSG将不会被编目)。
//SAMPINST JOB 1,CLASS=6,MSGCLASS=Y,NOTIFY=&SYSUID
//*
//STEP1 EXEC PGM=MYPROG
//IN1 DD DSN=MYDATA.URMI.INPUT,DISP=SHR
//OUT1 DD SYSOUT=*
//SYSOUT DD DSN=MYDATA.URMI.SYSOUT,DISP=SHR
//SYSPRINT DD DSN=MYDATA.URMI.SYSPRINT,DISP=SHR
//SYSIN DD MYDATA.BASE.LIB1(DATA1),DISP=SHR
//*
//STEP2 EXEC PGM=SORT
在上面的示例中,SYSOUT在MYDATA.URMI.SYSOUT和MYDATA.URMI.SYSPRINT中的SYSPRINT中编目。
Running COBOL Programs using JCL
编译COBOL程序
为了使用JCL以批处理模式执行COBOL程序,需要编译程序并使用所有子程序创建加载模块。 JCL在执行时使用加载模块而不是实际程序。 在使用JCLLIB或STEPLIB执行时,将加载库连接并提供给JCL。
有许多主机编译器实用程序可用于编译COBOL程序。 一些公司公司使用像Endevor这样的变更管理工具,它可以编译和存储程序的每个版本。 这在跟踪对程序所做的更改时很有用。
//COMPILE JOB ,CLASS=6,MSGCLASS=X,NOTIFY=&SYSUID
//*
//STEP1 EXEC IGYCRCTL,PARM=RMODE,DYNAM,SSRANGE
//SYSIN DD DSN=MYDATA.URMI.SOURCES(MYCOBB),DISP=SHR
//SYSLIB DD DSN=MYDATA.URMI.COPYBOOK(MYCOPY),DISP=SHR
//SYSLMOD DD DSN=MYDATA.URMI.LOAD(MYCOBB),DISP=SHR
//SYSPRINT DD SYSOUT=*
//*
IGYCRCTL是IBM COBOL编译器实用程序。 使用PARM参数传递编译器选项。 在上面的示例中,RMODE指示编译器在程序中使用相对寻址模式。 COBOL程序使用SYSIN参数传递,副本是SYSLIB中程序使用的库。
该JCL生成程序的加载模块作为输出,用作执行JCL的输入。
运行COBOL程序
在JCL示例下面,使用输入文件MYDATA.URMI.INPUT执行程序MYPROG,并生成两个写入假脱机的输出文件。
//COBBSTEP JOB CLASS=6,NOTIFY=&SYSUID
//
//STEP10 EXEC PGM=MYPROG,PARM=ACCT5000
//STEPLIB DD DSN=MYDATA.URMI.LOADLIB,DISP=SHR
//INPUT1 DD DSN=MYDATA.URMI.INPUT,DISP=SHR
//OUT1 DD SYSOUT=*
//OUT2 DD SYSOUT=*
//SYSIN DD *
//CUST1 1000
//CUST2 1001
/*
MYPROG的加载模块位于MYDATA.URMI.LOADLIB中。 请务必注意,上述JCL仅可用于非DB2 COBOL模块。
将数据传递给COBOL程序
输入到COBOL批处理程序的数据可以是通过文件,PARAM参数和SYSIN DD语句。 在上面的例子中:
数据记录通过文件MYDATA.URMI.INPUT传递给MYPROG。 该文件将使用DD名称INPUT1在程序中引用。 该文件可以在程序中打开,读取和关闭。
PARM参数数据ACCT5000在程序MYPROG的LINKAGE部分中以该部分中定义的变量接收。
SYSIN语句中的数据是通过程序的PROCEDURE分区中的ACCEPT语句接收的。 每个ACCEPT语句将一整条记录(即CUST1 1000)读入程序中定义的工作存储变量。
运行COBOL-DB2程序
对于运行COBOL DB2程序,在JCL和程序中使用专用的IBM实用程序; DB2区域和必需参数作为输入传递给实用程序。
在运行COBOL-DB2程序时遵循以下步骤:
编译COBOL-DB2程序时,将与加载模块一起创建DBRM(数据库请求模块)。 DBRM包含COBOL程序的SQL语句,其语法已检查正确。
DBRM绑定到将运行COBOL的DB2区域(环境)。 这可以使用JCL中的IKJEFT01实用程序来完成。
在绑定步骤之后,使用IKJEFT01(再次)运行COBOL-DB2程序,其中加载库和DBRM库作为JCL的输入。
//STEP001 EXEC PGM=IKJEFT01
//*
//STEPLIB DD DSN=MYDATA.URMI.DBRMLIB,DISP=SHR
//*
//input files
//output files
//SYSPRINT DD SYSOUT=*
//SYSABOUT DD SYSOUT=*
//SYSDBOUT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//DISPLAY DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
DSN SYSTEM(SSID)
RUN PROGRAM(MYCOBB) PLAN(PLANNAME) PARM(parameters to cobol program) -
LIB('MYDATA.URMI.LOADLIB')
END
/*
在上面的示例中,MYCOBB是使用IKJEFT01运行的COBOL-DB2程序。 请注意,程序名称,DB2子系统标识(SSID),DB2计划名称在SYSTSIN DD语句中传递。 DBRM库在STEPLIB中指定。
JCL - Utility Programs
IBM数据集实用程序
实用程序是预编程的程序,系统程序员和应用程序开发人员在大型机中广泛使用,以实现日常需求,组织和维护数据。 下面列出了其中一些功能:
效用名称 | 功能 |
---|---|
IEHMOVE | 移动或复制连续数据集。 |
IEHPROGM | 删除和重命名数据集; 除VSAM之外的目录或非目录数据集。< |
IEHCOMPR | 比较连续数据集中的数据。 |
IEBCOPY | 复制,合并,压缩,备份或恢复PDS。 |
IEFBR14 | 无操作实用程序。 用于将控制权返回给用户并终止。 它通常用于创建空数据集或删除现有数据集。 例如,如果使用DISP =(OLD,DELETE,DELETE)将数据集作为输入传递给IEFBR14程序,则在作业完成时删除数据集。 |
IEBEDIT | 用于复制JCL的选定部分。 例如,如果JCL有5个步骤并且我们只需要执行步骤1和3,则可以使用包含要执行的实际JCL的数据集对IEBEDIT JCL进行编码。 在IEBEDIT的SYSIN中,我们可以指定STEP1和STEP3作为参数。 执行此JCL时,它将执行实际JCL的STEP1和STEP3。 |
IDCAMS | 创建,删除,重命名,编目,取消编目数据集(PDS除外)。 通常用于管理VSAM数据集。 |
这些实用程序需要与JCL中的适当DD语句一起使用才能实现指定的功能。
DFSORT概述
DFSORT是一个功能强大的IBM实用程序,用于复制,排序或合并数据集。 SORTIN和SORTINnn DD语句用于指定输入数据集。 SORTOUT和OUTFIL语句用于指定输出数据。
SYSIN DD语句用于指定排序和合并条件。 DFSORT通常用于实现以下功能:
按文件中指定字段位置的顺序对输入文件进行排序。
根据指定的条件输入文件中的INCLUDE或OMIT记录。
SORT MERGE输入文件按文件中指定字段的顺序排列。
SORT根据指定的JOIN KEY(每个输入文件中的字段)连接两个或多个输入文件。
当对输入文件进行其他处理时,可以从SORT程序调用USER EXIT程序。 例如,如果要将一个标题/尾部添加到输出文件中,则可以从SORT程序调用USER编写的COBOL程序来执行此功能。 使用控制卡,可以将数据传递给COBOL程序。
反过来,可以从COBOL程序内部调用SORT,以便在处理之前按特定顺序排列输入文件。 通常,建议不要考虑大文件的性能。
ICETOOL概述
ICETOOL是一个多用途DFSORT实用程序,用于对数据集执行各种操作。 可以使用用户定义的DD名称定义输入和输出数据集。 文件操作在TOOLIN DD语句中指定。 可以在用户定义的“CTL”DD语句中指定其他条件。
下面给出了ICETOOL的一些实用工具:
ICETOOL可以在一个或多个条件下实现DFSORT的所有功能。
SPLICE是ICETOOL的强大操作,类似于SORT JOIN,但具有其他功能。 它可以比较指定字段上的两个或多个文件,并创建一个或多个输出文件,如具有匹配记录的文件,具有不匹配记录的文件等。
特定位置的一个文件中的数据可以被覆盖到相同或不同文件中的另一个位置。
可以根据指定的条件将文件拆分为n个文件。 例如,包含员工姓名的文件可以拆分为26个文件,每个文件包含以A,B,C等开头的名称。
使用ICETOOL可以对文件进行不同的文件操作组合,并对该工具进行一些探索。
SYNCSORT概述
SYNCSORT用于以高性能复制,合并或排序数据集。 它可以在31位和64位地址空间中充分利用系统资源和高效运算。
它可以在DFSORT的相同行中使用,并且可以实现相同的功能。 它可以由JCL调用,也可以在用COBOL,PL/1或汇编语言编写的程序中调用。 它还支持从SYNCSORT程序调用用户出口程序。
使用这些实用程序的常用排序技巧将在下一章中介绍。 需要在COBOL/ASSEMBLER中进行大量编程的复杂需求可以通过简单的步骤使用上述实用程序来实现。
JCL - Basic Sort Tricks
企业界的日常应用程序要求可以使用实用程序实现,如下所示:
1. A file has 100 records. The first 10 records need to be written to output file.
//JSTEP020 EXEC PGM=ICETOOL
//TOOLMSG DD SYSOUT=*
//DFSMSG DD SYSOUT=*
//IN1 DD DSN=MYDATA.URMI.STOPAFT,DISP=SHR
//OUT1 DD SYSOUT=*
//TOOLIN DD *
COPY FROM(IN1) TO(OUT1) USING(CTL1)
/*
//CTL1CNTL DD *
OPTION STOPAFT=10
/*
选项STOPAFT将在第10条记录后停止读取输入文件并终止程序。 因此,10个记录被写入输出。
2. Input file has one or more records for same employee number. Write unique records to output.
//STEP010 EXEC PGM=SORT
//SYSOUT DD SYSOUT=*
//SORTIN DD DSN=MYDATA.URMI.DUPIN,DISP=SHR
//SORTOUT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=(1,15,ZD,A)
SUM FIELDS=NONE
/*
SUM FIELDS = NONE删除SORT FIELDS中指定字段的重复项。 在上面的示例中,员工编号位于字段位置1,15。 输出文件将包含按升序排序的唯一员工编号。
3. Overwrite input record content.
//JSTEP010 EXEC PGM=SORT
//SORTIN DD DSN= MYDATA.URMI.SAMPLE.MAIN,DISP=SHR
//SORTOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
OPTION COPY
INREC OVERLAY=(47:1,6)
/*
在输入文件中,位置1,6的内容被覆盖到位置47,6,然后被复制到输出文件。 INREC OVERLAY操作用于在复制到输出之前重写输入文件中的数据。
4. Adding a sequence number to the output file.
//JSTEP010 EXEC PGM=SORT
//SORTIN DD *
data1
data2
data3
/*
//SORTOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
OPTION COPY
BUILD=(1:1,5,10:SEQNUM,4,ZD,START=1000,INCR=2)
/*
输出将是:
data1 1000
data2 1002
data3 1004
在位置10的输出中添加4位序列号,从1000开始并且对于每个记录递增2。
5. Adding Header/Trailer to output file.
//JSTEP010 EXEC PGM=SORT
//SORTIN DD *
data1
data2
data3
/*
//SORTOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
SORT FIELDS=COPY
OUTFIL REMOVECC,
HEADER1=(1:C'HDR',10:X'020110131C'),
TRAILER1=(1:C'TRL',TOT=(10,9,PD,TO=PD,LENGTH=9))
/*
输出将是:
HDR 20110131
data1
data2
data3
TRL 000000003
TOT计算输入文件中的记录数。 HDR和TRL作为标识符添加到标题/预告片中,标题/预告片是用户定义的,可以根据用户的需要进行自定义。
6. Conditional Processing
//JSTEP010 EXEC PGM=SORT
//SORTIN DD *
data1select
data2
data3select
/*
//SORTOUT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT DD SYSOUT=*
//SYSIN DD *
INREC IFTHEN=(WHEN=(6,1,CH,NE,C' '),BUILD=(1:1,15),
IFTHEN=(WHEN=(6,1,CH,EQ,C' '),BUILD=(1:1,5,7:C'EMPTY ')
OPTION COPY
/*
输出将是:
data1select
data2 EMPTY
data3select
根据文件的第6个位置,输出文件的BUILD会有所不同。 如果第6个位置是SPACES,则文本“EMPTY”将附加到输入记录。 否则,输入记录按原样写入输出。
7. Backing up a file
//JSTEP001 EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
//SYSOUT DD SYSOUT=*
//SORTOUT DD DUMMY
//SYSUT1 DD DSN=MYDATA.URMI.ORIG,DISP=SHR
//SYSUT2 DD DSN=MYDATA.URMI.BACKUP,DISP=(NEW,CATLG,DELETE),
// DCB=*.SYSUT1,SPACE=(CYL,(50,1),RLSE)
IEBGENER将SYSUT1中的文件复制到SYSUT2中的文件中。 请注意,SYSUT2中的文件与上例中的SYSUT1具有相同的DCB。
8. File Comparison
//STEP010 EXEC PGM=SORT
//MAIN DD *
1000
1001
1003
1005
//LOOKUP DD *
1000
1002
1003
//MATCH DD DSN=MYDATA.URMI.SAMPLE.MATCH,DISP=OLD
//NOMATCH1 DD DSN=MYDATA.URMI.SAMPLE.NOMATCH1,DISP=OLD
//NOMATCH2 DD DSN=MYDATA.URMI.SAMPLE.NOMATCH2,DISP=OLD
//SYSOUT DD SYSOUT=*
//SYSIN DD *
JOINKEYS F1=MAIN,FIELDS=(1,4,A)
JOINKEYS F2=LOOKUP,FIELDS=(1,4,A)
JOIN UNPAIRED,F1,F2
REFORMAT FIELDS=(?,F1:1,4,F2:1,4)
OPTION COPY
OUTFIL FNAMES=MATCH,INCLUDE=(1,1,CH,EQ,C'B'),BUILD=(1:2,4)
OUTFIL FNAMES=NOMATCH1,INCLUDE=(1,1,CH,EQ,C'1'),BUILD=(1:2,4)
OUTFIL FNAMES=NOMATCH2,INCLUDE=(1,1,CH,EQ,C'2'),BUILD=(1:2,4)
/*
JOINKEYS指定比较两个文件的字段。
REFORMAT FIELDS=? 在输出BUILD的第一个位置放置'B'(匹配的记录),'1'(存在于file1中,但不存在于file2中)或'2'(存在于file2中但不存在于file1中)。
JOIN UNPAIRED对这两个文件进行完全外连接。
输出将是:
<b class="notranslate">MATCH File</b>
1000
1003
<b class="notranslate">NOMATCH1 File</b>
1001
1005
<b class="notranslate">NOMATCH2 File</b>
1002
使用ICETOOL也可以实现相同的功能。