2.13 系统例程(Utility)及常用工具
一个例程可以理解为一个程序,为系统的运作、组织、维护去执行一个或多个功能。例程程序分为3类。
● 系统功能程序:用来罗列或修改与数据集和卷有关的信息,像数据集名、编目的输入、卷标。
✧ IEHINITT:在带卷上写入标准的标号。
✧ IEHLIST:列出所有控制数据的种类。
✧ IEHMOVE:移动和复制所获取的数据。
✧ IEHPROGM:建立和维护系统控制数据。
✧ IFHSTATR:从IFASMFDP磁带上选择、格式化、编写有关磁带错误的信息。
✧ IDCAMS:定义VSAM数据集,删除所有数据集,为数据集和卷的维护执行一系列功能,包括高级设备的硬件管理功能。
● 数据集例程程序:用来组织、修改或比较数据集或记录层上的数据,这些功能使用户熟练地控制分区的、顺序的、索引的顺序数据集,或分区数据集的扩展(PDSEs),它为程序提供输入信息。控制数据的范围是从逻辑记录到整个数据集。
✧ IEBCOMPR:比较分区数据集、顺序数据集或PDSEs的记录。
✧ IEBCOPY:复制、压缩或合并分区数据集或PDSEs,查询或排除再复制操作中的指定作业,重命名或更换分区数据集或PDSEs中的选择成员。
✧ IEBDG:创建由样品数据组建的测试数据集。
✧ IEBEDIT:选择性地复制作业步和与作业有关的JOB语句。
✧ IEBGENER:从顺序数据集里复制记录,或从顺序数据集转换到分区数据集。
✧ IEBIMAGE:为使用IBM 3800打印机子系统、IBM 3262模型5、4248打印机去修改、打印或连接模板。
✧ IEBISAM:卸载、装载、复制、打印ISAM数据集。
✧ IEBPTPCH:打印或输出顺序数据集或分区数据集里的记录。
✧ IEBUPDTE:合并修改顺序数据集、分区数据集或PDSEs。
● OS/390专用程序:数据分类(DFSORT)是IBM数据设备家族产品的成员,它是高性能数据安排设备。
✧ DFSORT:可以用来排序、合并、复制数据集。DFSORT可以执行一个简单的任务,像名单按字母排序,也可以作为复杂任务的辅助工具,像盘存或运行一个票据系统,DFSORT记录层的编辑功能可以用来执行数据管理任务。
大多数的例程程序是永久有效的,因此有一些例程程序的功能在现在比新出的应用得到更好的利用,IBM为什么继续推广例程程序的主要原因,是为了为早期操作系统的运行提供兼容性。
例程(Utility)程序是操作系统提供的一种通用程序。它们可以帮助你在数据集层面或数据集记录层面对数据集重组(Reorganize)、比较或修改。
例程程序已经使用了超过半个世纪。几十年前设计的某些例程功能依然比今天使用的ISPF/PDF功能要强。无论你的公司安装的MVS系统带有什么样的新功能,传统的例程程序依然能给你带来帮助。
2.13.1 选择需要的例程
选择你要使用的例程是很容易的,因为你的随机手册有一张表列出了每个例程所能完成的功能。表2.3列出了其中一部分常用的功能,有的任务可以由多个例程完成,你可以选择你喜欢的例程去完成所要的功能。
表2.3 常用的系统例程及功能
2.13.2 例程使用的ddname
不同的例程使用的ddname是不同的。我们这里只描述下面两种例程使用的ddname。
● IEB开头的例程使用的DD语句的ddnames为SYSPRINT、SYSUT1、SYSUT2和SYSIN。
● IEH开头的例程可以使用你自己喜欢的ddname。实际使用的ddname要在例程控制语句中指定。
根据例程要完成的功能不同,其所操作的DD语句对应的数据集可以定义为顺序数据集、分区数据集或分区数据集中的成员。
IEB例程程序DD语句使用的ddname为:
● SYSPRINT——定义系统动作和错误信息输出的数据集。
● SYSUT1——指定所有IEB开头的例程要处理的输入数据集,你需要在DD语句中提供该数据集的DCB子参数。
● SYSUT2——指定例程新建立的输出数据集。你需要指定输出数据集的记录长度、记录格式和分块大小等DCB信息。比如:
//SYSUT2 DD ...,DCB=(LRECL=...,RECFM=...,BLKSIZE=...)
● SYSIN——定义控制语句数据集,用来存放例程所要使用的控制语句。通常,控制语句直接使用内部流数据。如果你不需要考虑如何控制语句,你应该像下面这样编码。
//SYSIN DD DUMMY
对于由IEH开头的例程,你可以指定任何合法的ddname。
2.13.3 例程控制语句
例程控制语句用来告诉例程需要完成的任务。有些情况下,控制语句也告诉例程要处理的数据集。每个例程都有一组自己的控制语句。
比如,在我们后面的IEBGENER例程中就要使用下面的控制语句:
GENERATE MAXFLDS=2 RECORD FIELD=(14,1,,1),FIELD=(15,28,,15)
请注意,例程控制语句不是JCL语句,因而不用遵守JCL语句的语法规则。图2.26列出了例程控制语句的一般格式,如下:
图2.26 例程控制语句的一般格式
除了IEBUPDTE例程以外,所有例程的控制语句都遵守上面所示的通用格式。这里:
● 标号字段(LABEL):为控制语句起个名字,绝大多数情况下,标号字段都不是必需的。
● 操作符字段(OPERATION):说明控制语句的类型。
● 参数字段(OPERAND):由一个或多个关键字参数组成,参数之间用逗号(,)隔开。
控制语句的编写位置的规定为:
● 控制语句以内部流数据的格式在第2列到第71列编写。
● 如果控制语句一行写不完要在下一行继续,就必须在第72列写一个非空格字符。
● 控制语句续行要从下一行的第16列开始编写。
例程执行时会检查你提供的控制语句的语法和内容的合法性,如果有语法错误,你应该考虑:
● 标号、操作符和参数是否在正确的列开始。
● 续行语句的格式写得是否正确。
● 参数的格式是否正确,是用逗号隔开的吗。
2.13.4 复制和打印顺序数据集
IEBGENER是一个例程,既可以对顺序数据集操作又可以对分区数据集操作,它可以用来:
● 备份顺序数据集(SEQuential Data Set)或分区数据集(PDS)的成员(member)。
● 将顺序数据集转换成分区数据集、扩展的分区数据集或它们的成员。
● 将顺序数据集作为成员增加到分区数据集或扩展的分区数据集中。
● 通过建立分区成员或合并它们到一个已存在的数据集里,实现PDS或PDSE的扩充。
● 打印顺序数据集或分区数据集的成员。
● 重新编制或修改数据集的属性,如分块大小(BLKSIZE)等。
图2.27列出了IEBGENER例程的所有功能。
图2.27 IEBGENER例程的所有功能
下面的作业流是IEBGENER复制顺序数据集的例子,它会说明将在SYSUT1中的数据集IBMUSER.J37.OUTLIST复制到新建立的数据集IBMUSER.J37.COPY中,该数据集是由SYSUT2定义的。
000001 //IBMUSERA JOB (168),'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //STEP1 EXEC PGM=IEBGENER 000004 //SYSPRINT DD SYSOUT=* 000005 //SYSUT1 DD DSNAME=IBMUSER.J37.OUTLIST,DISP=SHR 000006 //SYSUT2 DD DSNAME=IBMUSER.J37.COPY,DISP=(,CATLG,DELETE), 000007 // SPACE=(TRK,(1,1)), 000008 // DCB=(RECFM=FB,LRECL=132,BLKSIZE=2640),UNIT=SYSDA 000009 //SYSIN DD DUMMY 000010 //
上面的作业流中的主要语句有:
● JOB——跟我们前面介绍过的一样,它告诉操作系统这是一个作业,其编码规则应该遵守你公司的相关规定。
● EXEC——指定要执行的例程程序是IEBGENER。
● SYSPRINT——指定由例程程序IEBGENER生成的信息要去的打印机的位置。这里我们指定的是SYSOUT=*,表示会写到JCL的输出流中。
● SYSIN——是程序IEBGENER需要的。但是,在这个例子中,由于不需要控制语句,因此SYSIN语句指定DUMMY。
● SYSUT1——指定输入数据集。这个例子指定的输入数据集是IBMUSER.J37.OUTLIST,它是存放在磁盘上的。
● SYSUT2——指定输出数据集。这个例子指定的是IBMUSER.J37.COPY,它存放在设备组SYSDA上。
下面的例子用来将输入数据集IBMUSER.J37.OUTLIST中的内容打印出来,其完整的作业流如下所示。读者可以看到,本作业流与上面的数据集复制作业流是几乎一样的,唯一不同的是SYSUT2。在该作业流中,SYSUT2指定输出数据集要去的打印机的位置即输出类别(Class),在这个例子中,我们指定的SYSOUT的类别为A,如第6行的语句所示。
000001 //IBMUSERA JOB (168),'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //STEP1 EXEC PGM=IEBGENER 000004 //SYSPRINT DD SYSOUT=* 000005 //SYSUT1 DD DSNAME=IBMUSER.J37.OUTLIST,DISP=SHR 000006 //SYSUT2 DD SYSOUT=A 000007 //SYSIN DD DUMMY 000008 //
2.13.5 编辑顺序数据集
假定有一个脱机编辑的数据集要作为某个作业的输入文件。这在银行系统中是常见的情况,比如,代发工资作业,是各家公司将他们员工的工资表做好后,用文本文件的形式交由银行处理。工资表记录格式通常都是由银行提供给代发工资公司的,正常情况下,代发工资公司都能提供正确格式的工资表文件给银行,这样,代发工资的处理就不会有什么问题。但是,公司提供给银行的工资表可能会有格式不对的情况,比如,某家公司上个月提供的工资表中,每个记录中都漏掉了两个字段,一个是公司编号,另一个是银行交易码,在银行规定的格式中,公司编号位于第1列到第4列,而银行交易代码则位于第5列到第7列。
对于处理代发工资的程序来说,对格式的要求是严格的,因此,漏掉的公司编号和银行交易码必须补上才能保证代发工资的正常运行。如果不修改数据,就要为这次错误的数据集专门修改程序,将其作为特例处理,这在系统维护中通常是不容许的,因为,没有人敢保证,你修改过的程序就能保证产生的结果是完全正确的。要知道,代发公司影响的不仅仅是银行,受影响的还有代发工资公司的全体员工。
例程程序IEBGENER可以帮我们完成这项重要的工作,因为它既可以复制,也可以编辑顺序数据集中的数据。在复制过程中,你可以使用IEBGENER将特定的文字变量放到记录的适当位置(列)上,这样代发工资程序就可以像以前一样正确处理它们了。在下面的例子中,我们需要将代发工资公司的编号(0138)放到文件的第1列到第4列,而将银行交易码(DEP)放到工资输入文件的第5列到第7列。
下面的例子说明了IEBGENER编辑数据的过程。这里,程序IEBGENER控制语句将公司编号(0138)和代发工资的银行交易代码(DEP)添加到遗漏掉的字段中。完整的作业流如下。
000001 //IBMUSERA JOB (168),'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //STEP1 EXEC PGM=IEBGENER 000004 //SYSPRINT DD SYSOUT=* 000005 //SYSUT1 DD DSNAME=IBMUSER.TEST.BKTRAN.SALARY,DISP=SHR 000006 //SYSUT2 DD DSNAME=IBMUSER.TEST.BKTRAN.SALARY.OUT, 000007 // DISP=(,CATLG,DELETE), 000008 // SPACE=(TRK,(1,1)), 000009 // DCB=(RECFM=FB,LRECL=80,BLKSIZE=2400),UNIT=SYSDA 000010 //SYSPRINT DD SYSOUT=A 000011 //SYSIN DD * 000012 GENERATE MAXFLDS=3,MAXLITS=7 000013 RECORD FIELD=(4,'0138',,1), 000014 FIELD=(3,'DEP',,5), 000015 FIELD=(73,8,,8) 000016 /* 000017 //
跟前面的例子一样,SYSUT1语句描述了要编辑的输入文件,在该文件中,公司编号和银行交易代码是漏掉没有输入的。SYSUT2描述的是经过IEBGENER编辑后的输出文件,经过编辑后,原来漏掉的公司编号和银行交易码都补充到正确的位置了。如果输出文件SYSUT2的格式跟输入文件SYSUT1的格式完全一样,SYSUT2中的DCB信息就可以不写,因为IEBGENER的默认动作就是将SYSUT1的属性复制到输出文件SYSUT2中。
在这个例子中,由于要对数据进行编辑,因此,必须使用例程控制语句。当使用IEBGENER编辑数据时,必须使用GENERATE控制语句。GENERATE控制语句必须出现在所有其他控制语句的前面。下面显示的是GENERATE语句的语法。
GENERATE [,MAXNAME=n] [,MAXFLDS=n] [,MAXGRPS=n] [,MAXLITS=n] [,DBCS={YES/NO}]
如果你要使用MEMBER控制语句,就必须指定参数MAXNAME,它说明出现在后面的MEMBER语句中的最大成员数。在我们的例子中,没有使用该参数。
如果要使用RECORD参数,就必须指定MAXFLDS参数,它用来说明在后面出现的RECORD参数中最多有几个字段。在我们的例子中,MAXFLDS的值为3,因为RECORD语句中使用了3个字段。如果在你后面的RECORD语句中要使用IDENT或IDENTG参数,你就必须指定MAXGRPS参数。
如果在你的FIELD参数中带有文字常数,就必须使用MAXLITS参数,它说明你所使用的文字常数的最大长度。如果使用汉字之类的双字节(Double Byte Character Set——DBCS)字符作为FIELD参数中的文字常数,每个汉字算2个字符。在我们的例子中,我们使用的MAXLITS的值为7。
如果数据集中包含双字节字符,如汉字,就必须指定DBCS={Yes/No}参数,在我们的例子中没有汉字,所以没有使用该参数。
在我们例子中的GENERATE语句使用的参数为:
● MAXFLDS指定出现在后面的RECORD语句中的字段的个数,其值为3。
● MAXLITS指定在FIELD参数中使用的最大文字字符的个数,其值为7。
完整的GENERATE语句出现在第12行,其内容为:
GENERATE MAXFLDS=3,MAXLITS=7
控制语句RECORD用来描述输出记录格式。参数FIELD指定每个要从输入文件中复制到输出文件的字段。字段的描述要用括号括起来。控制语句RECORD中的FIELD参数格式为:
RECORD FIELD=([LENGTH],’LITERAL’,[CONVERSION],[OUTPUT-LOCATION]) RECORD FIELD=(4,'0138',,1), FIELD=(3,'DEP',,5), FIELD=(73,8,,8)
在我们的例子使用的控制语句RECORD显示在上面。开始的两个FIELD参数指定要补充填写的常数字段,分别是公司编号和银行交易码。而第3个FIELD字段则表示要将输入记录其余部分(从第8列开始的总共73列内容)原封不动地复制到输出记录中。
2.13.6 转换顺序数据集为分区数据集的成员
在前面编辑顺序数据集的例子中,程序IEBGENER建立了一个顺序数据集。如果,我们希望将该顺序数据集转换为某个分区数据集(PDS)的成员,应该怎样做呢?
例程IEBGENER和IEBUPDTE都能完成这项工作。但IEBUPDTE只能处理最多80个字节长的记录,如果记录长度超过80,就必须使用IEBGENER。
假定下面的文件属性说明。
● 输入文件仍然为IBMUSER.TEST.BKTRAN.SALARY,是登目文件,且其DCB属性为:DCB=(RECFM=FB,LRECL=80,BLKSIZE=2400)。
● 输入数据集要复制到分区数据集IBMUSER.TEST.BKTRAN.SALARY.PDS中,其成员名为COP0138。
● 为新建立的分区数据集分配的空间为:4个目录块,第1次分配3个扇区,以后每次追加5个扇区。
● 输出数据集与输入数据集的属性相同。
● 输出数据集记录的编辑方法与上面编辑顺序文件的方法相同,即在原输入文件的第1列到第4列加上公司编号0138,在第5列到第7列加上银行交易码DEP。
下面是完成上述编辑任务的完整的作业流。
000001 //IBMUSERA JOB (168),'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //STEP1 EXEC PGM=IEBGENER 000004 //SYSPRINT DD SYSOUT=* 000005 //SYSUT1 DD DSNAME=IBMUSER.TEST.BKTRAN.SALARY,DISP=SHR 000006 //SYSUT2 DD DSNAME=IBMUSER.TEST.BKTRAN.SALARY.PDS, 000007 // DISP=(,CATLG,DELETE), 000008 // SPACE=(TRK,(3,5,4)) 000009 //* ------------------------------------------------------------------* 000010 //* THE FOLLOWING DCB CAN BE OMITED IF FILE WITH THE SAME ATTRIBUTION * 000011 //* AS INPUT FILE * 000012 //* ------------------------------------------------------------------* 000013 //*DCB=(RECFM=FB,LRECL=80,BLKSIZE=2400),UNIT=SYSDA 000014 //SYSPRINT DD SYSOUT=A 000015 //SYSIN DD * 000016 GENERATE MAXFLDS=3,MAXLITS=7, 000017 MAXNAME=1 000018 MEMBER NAME=COP0138 000019 RECORD FIELD=(4,'0138',,1), 000020 FIELD=(3,'DEP',,5), 000021 FIELD=(73,8,,8) 000022 /* 000023 //
请注意第6行到第8行的//SYSUT2 DD语句的SPACE参数为分区数据集分配空间的方法。SPACE参数的通用格式如下:
SPACE=(空间单位,(主分配,次分配,目录块))
在我们的例子中,SPACE参数的分配结果为:
除了上面的标准JCL语句外,我们还需要下面这些例程控制语句。
000016 GENERATE MAXFLDS=3,MAXLITS=7, 000017 MAXNAME=1 000018 MEMBER NAME=COP0138 000019 RECORD FIELD=(4,'0138',,1), 000020 FIELD=(3,'DEP',,5), 000021 FIELD=(73,8,,8)
这里的GENERATE语句说明要建立的输出数据集是分区数据集。其中:
MAXNAME表明要写到分区数据集中的新成员(Members)的个数,这里是1,即只写一个成员COP0138到新建立的分区数据集中。
MEMBER语句说明输出数据集是分区数据集。NAME指定成员的名字,这里是COP0138。
RECORD语句描述输出记录的格式,我们在上一个例子中已经做了说明,这里就不重复了。
2.13.7 转换顺序数据集到分区数据集——增加新成员
例程IEBGENER不仅可以建立新分区数据集,将顺序数据集转换成分区数据集的成员,而且可以在已有分区数据集中增加新的成员。
比如,在前面的例子中,我们将顺序文件IBMUSER.TEST.BKTRAN.SALARY编辑以后,建立了分区数据集IBMUSER.TEST.BKTRAN.SALARY.PDS,并将编辑好的文件作为成员COP0138存放到了分区数据集中。现在,假定我们要将编辑好的文件做一个备份,并作为成员名BKUP0138存放到已经建立的分区数据集中。我们可以使用上面的作业流,而只是修改一下SYSUT2的DD语句,就可以达到我们的要求。
满足要求的作业流如下面的例子所示。注意,在这个例子中,原来出现在SYSUT2中的SPACE参数没有了,因为分区数据集已经建立起来。也请注意SYSUT2的DISP参数变成了OLD,这也反映了分区数据集的存在。在第11行的控制语句中,MEMBER参数的NAME变成了BKUP0138,因为我们希望生成成员COP0138的备份,而不是覆盖原有的成员。
000001 //IBMUSERA JOB (168),'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //STEP1 EXEC PGM=IEBGENER 000004 //SYSPRINT DD SYSOUT=* 000005 //SYSUT1 DD DSNAME=IBMUSER.TEST.BKTRAN.SALARY,DISP=SHR 000006 //SYSUT2 DD DSNAME=IBMUSER.TEST.BKTRAN.SALARY.PDS,DISP=OLD 000007 //SYSPRINT DD SYSOUT=A 000008 //SYSIN DD * 000009 GENERATE MAXFLDS=3,MAXLITS=7, 000010 MAXNAME=1 000011 MEMBER NAME=BKUP0138 000012 RECORD FIELD=(4,'0138',,1), 000013 FIELD=(3,'DEP',,5), 000014 FIELD=(73,8,,8) 000015 /* 000016 //
2.13.8 复制分区数据集或分区数据集成员
IEBCOPY是一个数据集例程,专门用于管理分区数据集的。它具备下列功能。
● 分区数据集或扩展分区数据集的复制。
● 将分区数据集转换成扩展的分区数据集。
● 合并分区数据集。
● 卸载(备份)和装载(恢复)分区数据集或扩展的分区数据集。
● 复制、合并、装载或卸载时,选定或排除分区数据集或扩展分区数据集中的成员。
● 置换分区数据集或扩展分区数据集中的成员。
● 对选定的分区数据集或扩展分区数据集成员重命名。
● 对分区数据集进行适当的压缩。
● 分区数据集与扩展分区数据集之间的转换。
图2.28列出了IEBCOPY例程的所有功能。
图2.28 IEBCOPY例程的所有功能
下面的例子将分区数据集IBMUSER.TEST.JCL1复制到分区数据集IBMUSER.TET.JCL2中。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* COPY PARTITIONED DATA SET * 000005 //*---------------------------------------------------------------* 000006 //STEP1 EXEC PGM=IEBCOPY 000007 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 000008 //SYSUT2 DD DSN=IBMUSER.TEST.JCL2,DISP=(,CATLG,DELETE), 000009 // UNIT=SYSDA, 000010 // SPACE=(TRK,(3,5,4)), 000011 // DCB=*.SYSUT1 000012 //SYSPRINT DD SYSOUT=* 000013 //SYSIN DD DUMMY 000014 //
请注意,第10行的SPACE必须使用格式SPACE=(空间单位,(主分配,次分配,目录块)),而且目录块的值必须大于零,因为我们要建立的是分区数据集,如果不大于零,所建立的数据集就不是所希望的了。
下面的例子将分区数据集IBMUSER.TEST.JCL1中的成员CBLDB2和COMPBDB复制到我们上面新建立的分区数据集IBMUSER.TEST.JCL2中。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* COPY PARTITIONED DATA SET MEMBERS * 000005 //*---------------------------------------------------------------* 000006 //STEP1 EXEC PGM=IEBCOPY 000007 //IN DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 000008 //OUT DD DSN=IBMUSER.TEST.JCL2,DISP=OLD 000009 //SYSPRINT DD SYSOUT=* 000010 //SYSIN DD * 000011 COPY INDD=IN,OUTDD=OUT 000012 SELECT M=((CBLDB2,,R)) 000013 SELECT M=((COMPBDB,,R)) 000014 //
请注意,在这个例子中的DD语句没有使用SYSUT1和SYSUT2,因为我们使用了IEBCOPY例程的控制语句。因此,只要DD语句的说明与控制语句中的一致就可以了。现在对控制语句做个说明。
● COPY语句:第11行的COPY语句启动一个或多个复制、卸载或加载操作。其格式为:
label COPY OUTDD=ddname ,INDD=({ddname | (ddname,R)},…) ,LIST={YES|NO}
其中的子参数有:INDD=({ddname | (ddname,R)},…)
✧ OUTDD=ddname:指定输出的分区数据集名,这里的ddname必须是本作业步中的一个DD语句名。在我们的例子中指定输出数据集IBMUSER.TEST.JCL2。
✧ INDD=({ddname | (ddname,R)},…):指定输入分区数据集或扩展分区数据集,对于卸载操作只能指定一个ddname,这里的R表示从输入的数据集中选择所有的成员进行相应的操作,此时不需要设置SELECT语句。在我们的例子中指定输入数据集IBMUSER.TEST.JCL1。
✧ LIST={YES|NO}:指出是否将复制的成员名列在SYSPRINT数据集中,取值为YES时将成员名列在数据集中。
需要注意的是:
✧ 如果为复制操作,输入数据集和输出数据集必须是分区数据集、扩展分区数据集或卸载模块结果的顺序数据集。
✧ 如果是加载操作,输入数据集必须是分区数据集或顺序数据集,而输出数据集则必须是分区数据集。
✧ 如果是卸载操作,输入数据集必须是分区数据集、扩展分区数据集或顺序分区数据集,输出数据集可以驻留在直接存取卷上,也可以驻留在磁带卷上。当驻留在磁带卷上时,它的组织结构必须是顺序数据集,且要指定省略目录或缩影值。
● SELECT语句:第12行和第13行的SELECT确定输入数据集中要复制的成员名,其格式为:
label SELECT MEMBER=({name1|(name1,newname1,R)|(name1, ,R)}| ,(name2,newname2,R)|(name2,,R)},。。。)
操作数说明如下。
✧ MEMBER=…:name确定复制的成员名,newname确定复制输出的成员名,如果这个名字在输出分区数据集中已存在,则这个成员不被复制,除非同时设置R参数,R表示置换(REPLACE)输出数据集中已有的成员名,newname和ALTERMOD不能同时使用。在我们的例子中,要复制的成员是CBLDB2和COMPBDB,如果输出数据集中已有同名成员,就用输入数据集中的成员置换它们。
● EXCLUDE语句:确定输入数据集中不被复制和不加载的成员,其格式为:
label EXCLUDE MEMBER=(name1,name2,…)
2.13.9 分区数据集的压缩
当SYSUT1和SYSUT2指定相同的分区数据集名字时,IEBCOPY可以将该数据集进行压缩,其结果就像是PC上的磁盘整理功能。下面的例子将压缩数据集IBMUSER.TEST.JCL1。
000001 //IBMUSERA JOB (168),'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* COMPRESS DATASET 000005 //*---------------------------------------------------------------* 000006 //STEP1 EXEC PGM=IEBCOPY 000007 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 000008 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 000009 //SYSPRINT DD SYSOUT=* 000010 //SYSIN DD DUMMY 000011 //
2.13.10 维护源程序库
例程IEBUPDTE(Update Data Set)程序可以用来建立顺序数据集、分区数据集和扩展分区数据集,也可以修改已有的顺序数据集、分区数据集和扩展分区数据集的属性。例程IEBUPDTE主要用来修改过程库、源程序库和宏代码库,这些库中都存放的是源程序或宏代码等。
在下面的例子中,假定需要在我们的过程库IBMUSER.TEST.PROC1中增加一个成员MYIEB1,并为该成员中的每一行JCL语句编一个序号。这些序号是我们随后的修改过程语句时必需的。
下面是修改过程需求的说明。
● 过程库存放的是作业流。
● 输出数据集是过程库,名字为IBMUSER.TEST.PROC1,已经存在。
● 需要增加的成员名字为MYIEB1。
● 输出成员的每条JCL语句都要有个序号,开始序号为10,后面的序号按顺序编写并每次加10。
● 在作业流输出清单中显示每行记录的内容。
下面是完成该项任务的完整作业流。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* ADD NEW MEMBER TO PROCEDURE LIBS * 000005 //*---------------------------------------------------------------* 000006 //ADDIEB1 EXEC PGM=IEBUPDTE,PARM=MOD 000007 //SYSUT1 DD DSN=IBMUSER.TEST.PROC1,DISP=OLD 000008 //SYSUT2 DD DSN=IBMUSER.TEST.PROC1,DISP=OLD 000009 //SYSPRINT DD SYSOUT=A 000010 //SYSIN DD DATA 000011 ./ ADD LIST=ALL,NAME=MYIEB1 000012 ./ NUMBER NEW1=10,INCR=10 000013 //IBMUSERA JOB (36512),'IBMUSER',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000014 // NOTIFY=IBMUSER 000015 //*---------------------------------------------------------------* 000016 //* COMPRESS DATASET 000017 //*---------------------------------------------------------------* 000018 //STEP1 EXEC PGM=IEBCOPY 000019 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 000020 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 000021 //SYSPRINT DD SYSOUT=* 000022 //SYSIN DD DUMMY 000023 // 000024 ./ ENDUP 000025 /*
在上面的例子中,DD语句SYSUT1定义要修改的分区数据集,而DD语句SYSUT2定义修改以后的分区数据集。由于这是一个更新数据集的过程,所以SYSUT1和SYSUT2都指向了同一个数据集。所以,例程只是修改该数据集的内容。如果指向不同数据集的话,那么就会是一个先复制,然后再更新的过程。
IEBUPDTE所有特殊的例程控制语句,它们是由第1列和第2列的./字符开始的。这两个字符可以将控制语句与程序的输入数据流区分开来。
IEBUPDTE可以用来增加、修改或置换分区数据集中的成员。对应的控制语句分别是(./ ADD)、(./CHANGE)和(./ REPL)。
控制语句(./ ADD)用来增加一个成员到分区数据集中,其格式为:
./ ADD NAME=member, LIST=ALL
控制语句(./ CHANGE)用来删除、修改序号或增加数据到成员或数据集中。它后面一般跟着的语句有DELETE、NUMBER或数据语句。
控制语句(./ REPL)用来置换SYSUT2数据集中的成员。在我们的例子中使用的控制语句是第11行的(./ ADD LIST=ALL,NAME=MYIEB1),因为我们要增加一个新成员到过程库中。
控制语句(./ NUMBER)新过程分配序号的方法,其中,NEW1=指定起始序号,在我们的例子中是10;INCR=10表示随后的序号顺序编写且每次加10,比如,10、20、30等。
控制语句(./ ENDUP)标志输入语句结束了。请注意,控制语句中的开始字符./总是从第1列开始的。
在我们的例子中,需要下面的控制语句。
第11行的(./ ADD)表示要增加新成员MYIEB1到分区数据集中。LIST=ALL是可选项,用来将整个成员中的内容显示在SYSPRINT中。NAME=指定新建立的成员的名字及MYIEB1。
第13行到第23行中的内容是我们要输入到新成员中的作业语句的全部内容,我们用斜体表示。这里要注意第10行的SYSIN DD DATA语句,你应该明白为什么这里不能使用SYSIN DD *,因为在我们的内部输入流中包含了JCL语句,SYSIN DD DATA只有碰到/*才会结束输入流,而SYSIN DD *只要碰到下一条JCL语句就会结束内部数据流。
下面显示的是执行上面的作业流后新建立的成员MYIEB1中的内容。你可以很容易看出它们是从我们作业流中的输入得来的。请注意,在每一行的第73列到第80列的序号,从10开始,每次加10,它们在我们接下来的修改作业流的例子中是必需的。
第73列
000001 //IBMUSERA JOB (36512),'IBMUSER',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 00000010
000002 // NOTIFY=IBMUSER 00000020
000003 //*---------------------------------------------------------------* 00000030
000004 //* COMPRESS DATASET 00000040
000005 //*---------------------------------------------------------------* 00000050
000006//STEP1 EXEC PGM=IEBCOPY 00000060
000007//SYSUT1 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 00000070
000008 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL1,DISP=SHR 00000080
000009 //SYSPRINT DD SYSOUT=* 00000090
000010 //SYSIN DD DUMMY 00000100
000011 // 00000110
在下面的例子中,我们要将刚建立的成员MEIEB1中的数据集IBMUSER.TEST.JCL1改成IBMUSER. TEST.JCL2。所使用的控制语句出现在作业流的第11行到第16行,其中,第11行的CHANGE语句表明要修改成员MYIEB1中的内容,接下来第12行的NUMBER语句告诉了新的序号编写方法,即对于修改后的成员中的语句编号从15开始,每次增加15。第13行和第14行语句表示新语句的内容,它们要修改的原来行的编号分别是原来的0000070行和00000080行,回过头看看原来的内容,编号为00000070和00000080行的内容就是我们要修改的SYSUT1和SYSUT2。第15行的控制语句(./ ENDUP)结束控制语句的输入。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* ADD NEW MEMBER TO PROCEDURE LIBS * 000005 //*---------------------------------------------------------------* 000006 //ADDIEB1 EXEC PGM=IEBUPDTE,PARM=MOD 000007 //SYSUT1 DD DSN=IBMUSER.TEST.PROC1,DISP=OLD 000008 //SYSUT2 DD DSN=IBMUSER.TEST.PROC1,DISP=OLD 000009 //SYSPRINT DD SYSOUT=A 000010 //SYSIN DD DATA 000011 ./ CHANGE NAME=MYIEB1,LIST=ALL 000012 ./ NUMBER SEQ1=ALL,NEW1=15,INCR=15 000013 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL2 00000070 000014 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL2 00000080 000015 ./ ENDUP 000016 /* 000017 //
下面显示的是执行完上面的例程IEBUPDTE后,MYIEB1中的内容,你可以看到,序号的编写方法变了,而原来的SYSUT1和SYSUT2对应的数据集IBMUSER.TEST.JCL1已经改成IBMUSER.TEST.JCL2了。
000001 //IBMUSERA JOB (36512),'IBMUSER',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 00000015 000002 // NOTIFY=IBMUSER 00000030 000003 //*---------------------------------------------------------------* 00000045 000004 //* COMPRESS DATASET 00000060 000005 //*---------------------------------------------------------------* 00000075 000006//STEP1 EXEC PGM=IEBCOPY 00000090 000007 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL2 00000105 000008 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL2 00000120 000009 //SYSPRINT DD SYSOUT=* 00000135 000010 //SYSIN DD DUMMY 00000150 000011 // 00000165
我们需要说明的是,IEBUPDATE最有代表性的用法是用做程序的发布和维护。比方说,如果需要给客户的程序库中添加25个JCL过程,我们只需要将这25个过程作为一个程序包用IEBUPDATE导入。这样做的一个好处是由于所有的产品都使用一种源程序格式,客户就可以很方便地在使用之前进行审核。
2.13.11 打印分区数据集(PDS)目录清单
下面要介绍的例程程序IEHLIST是用来打印分区数据集(PDS)目录的内容的,目的是为了知道到底有哪些成员已经存在于分区数据集中。IEHLIST(List System Data)例程可以为:
● 列出分区数据集目录中的成员清单。
● 列出直接存储设备的VTOC(Volume Table of Contents)内容。
在IEHLIST例程中使用的DD名是SYSIN和SYSPRINT。但是,用户可以使用自己的ddname来定义要处理的磁盘卷。这些DD语句的目的是提供足够的信息使得系统可以找到所要处理的卷。
在IEHLIST例程中,指定数据集名字的DD语句是放在例程控制语句,而不是编写在DD语句中的。控制语句也要指定数据集的设备类型和卷序列号。
下面的完整作业流用来列出分区数据集目录中的内容。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //LISTPDS EXEC PGM=IEHLIST 000004 //SYSPRINT DD SYSOUT=A 000005 //DD1 DD UNIT=3390,VOLUME=REF=SYS1.NUCLEUS,DISP=OLD 000006 //DD2 DD UNIT=3390,VOLUME=SER=DMTP02,DISP=OLD 000007 //SYSIN DD * 000008 LISTPDS DSNAME=IBMUSER.TEST.JCL1,VOLUME=3390=DMTP02 000009 LISTPDS DSNAME=(SYS1.HELP,SYS1.SAMPLIB),FORMAT 000010 /* 000011 //
控制语句的说明如下。
● DD语句DD1定义系统所在的磁盘卷,通常是在DMTRES卷上,但每家公司的定义可能有些不同,但像上面那样指向存放系统内核文件SYS1.NUCLEUS的卷通常都是正确的。
● DD语句DD2定义磁盘卷DMTP02,它是我们后面要查看的数据集IBMUSER.TEST.JCL1所在的卷。
● DD语句SYSIN定义控制语句,我们使用的是内部数据流,直接放在JCL语句中。
● 控制语句LISTPDS用来列出分区数据集的目录内容。
● 第8行的第1条LISTPDS语句指明要列出的是关于数据集IBMUSER.TEST.JCL1相关的分区数据集目录内容,也就是列出该数据集的所有成员的清单,包括它们的名字等。列出清单的格式是非编辑格式,即转存(Dump)格式。该数据集存放在磁盘卷DMTP02上。
● 第9行的第2条LISTPDS语句列出的是分区数据集SYS1.HELP和SYS1.SAMPLIB的目录内容。列出内容的格式为可编辑格式,它们存放在系统常驻磁盘卷上。
例程IEHLIST经常使用的另外一个控制语句是LISTVTOC,它用来列出磁盘卷的VTOC(Volume Table of Contents)信息。其格式为:
LISTVTOC VOL=device=serial,option,… LISTVTOC FORMAT,VOL=3390=DMTP02
上面的例子列出直接存取设备(DASD)DMTP02卷上的VTOC信息。FORMAT选项表示用编辑形式列出VTOC的内容。
下面是LISTVTOC控制语句的完整例子。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //LISTPDS EXEC PGM=IEHLIST 000004 //SYSPRINT DD SYSOUT=A 000005 //DD1 DD UNIT=3390,VOLUME=SER=DMTP02,DISP=OLD 000006 //DD2 DD UNIT=3390,VOLUME=SER=DMTOS1,DISP=OLD 000007 //SYSIN DD * 000008 LISTVTOC FORMAT,VOL=3390=DMTP02 000009 LISTVTOC DUMP,VOL=3390=DMTOS1,DSNAME=(IBMUSER.TEST.JCL2) 000010 /* 000011 //
我们对上面的控制语句做以下说明。
● DD语句DD1定义包含特定VTOC信息的卷,这是我们要查看的卷之一。
● DD语句DD2定义我们要查看的另外一个卷。
● DD语句SYSIN定义控制语句数据集,我们仍然使用内部数据流,即控制语句直接写在JCL中。
● 第8行的第1条LISTVTOC语句以编辑格式列出磁盘卷DMTP02中的VTOC内容。
● 第9行的第2条LISTVTOC语句列出的是数据集IBMUSER.TEST.JCL2的控制块信息,列出的格式为非编辑即转存(Dump)的格式。
2.13.12 比较分区数据集
例程IEBCOMPR可以用来比较顺序数据集或分区数据集中的逻辑记录,通常用来验证备份的正确性。
下面的例子是用来比较分区数据集的。如果两个数据集中成员个数或成员名不同时,系统会将结果显示出来并将返回码置为12,如果完全相同,则返回码为0。下面是比较两个分区数据集的例子。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* COMPARE PARTITIONED DATA SET * 000005 //*---------------------------------------------------------------* 000006 //STEP1 EXEC PGM=IEBCOMPR 000007 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL,DISP=SHR 000008 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL2,DISP=SHR 000009 //SYSPRINT DD SYSOUT=* 000010 //SYSIN DD * 000011 COMPARE TYPORG=PO 000012 /* 000013 //
控制语句的说明如下。
● DD语句SYSUT1定义分区数据集IBMUSER.TEST.JCL1,存放在磁盘上。
● DD语句SYSUT2定义分区数据集IBMUSER.TEST.JCL2,也是存放在磁盘上。
● DD语句SYSIN定义控制语句数据集,它是内部数据流,直接写在JCL中。
● 控制语句COMPARE TYPORG=PO说明要比较的两个数据集是分区数据集。
2.13.13 比较顺序数据集
下面的例子比较两个顺序数据集。如果两个数据集有不同的记录内容时,系统的返回码为8,如果两个文件的记录内容完全相同时,系统的返回码就是0。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* COMPARE PARTITIONED DATA SET * 000005 //*---------------------------------------------------------------* 000006 //STEP1 EXEC PGM=IEBCOMPR 000007 //SYSUT1 DD DSNAME=IBMUSER.TEST.BKMASTN,DISP=SHR 000008 //SYSUT2 DD DSNAME=IBMUSER.TEST.BKMASTO,DISP=SHR 000009 //SYSPRINT DD SYSOUT=* 000010 //SYSIN DD * 000011 COMPARE TYPORG=PS 000012 /* 000013 //
控制语句的说明如下。
● DD语句SYSUT1定义顺序数据集IBMUSER.TEST.BKMASTN,存放在磁盘上。
● DD语句SYSUT2定义顺序数据集IBMUSER.TEST.BKMASTO,也是存放在磁盘上。
● DD语句SYSIN定义控制语句数据集,它是内部数据流,直接写在JCL中。
● 控制语句COMPARE TYPORG=PS指定要比较的输入文件为顺序文件。
2.13.14 比较分区数据集中的成员
下面的例子比较两个分区数据集中的成员。当两个成员的内容不同时,系统的返回码为12;当两个文件的记录内容完全相同时,系统的返回码就是0。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*---------------------------------------------------------------* 000004 //* COMPARE PARTITIONED DATA SET * 000005 //*---------------------------------------------------------------* 000006 //STEP1 EXEC PGM=IEBCOMPR 000007 //SYSUT1 DD DSNAME=IBMUSER.TEST.JCL(CBLCICS),DISP=SHR 000008 //SYSUT2 DD DSNAME=IBMUSER.TEST.JCL2(CBLDB2),DISP=SHR 000009 //SYSPRINT DD SYSOUT=* 000010 //SYSIN DD * 000011 COMPARE TYPORG=PS 000012 /* 000013 //
控制语句的说明如下。
● DD语句SYSUT1定义输入数据集IBMUSER.TEST.JCL中的成员CLBCICS。
● DD语句SYSUT2定义输入数据集IBMUSER.TEST.JCL2中的成员CBLDB2。
● DD语句SYSIN定义控制语句数据集,它是内部数据流,直接写在JCL中。
● 当比较分区数据集的成员时,控制语句要写成COMPARE TYPORG=PS,即要求系统当成顺序文件处理。
2.13.15 顺序文件的建立和删除
程序IEFBR14不执行任何实际功能,它由两条程序语句构成:第1是清除寄存器15,它的返回码是0;第2是分支到寄存器14的地址,它把控制权返回给系统。如果一个作业步骤执行程序IEFBR14,系统就会采取下面的动作。
● 检查步骤中所有作业控制语句的语法。
● 为数据集直接分配存取空间。
● 执行数据集处置(Disposition)处理。
即使IEFBR14没有任何实际的功能,但执行它可以将一组JCL语句在作业步中集中起来,达到对数据集处置的效果。IEFBR14最有代表性的使用是分配或删除随后作业步中所需的数据集。一般用法为:
下面的例子删除(Delete)两个QSAM文件,以避免JCL执行时由于数据集已经存在导致的JCL错误。
//DFQSAM1 EXEC PGM=IEFBR14 //BLQML49 DD DSN=IBMUSER.DHB.BLQML49, // DISP=(MOD,DELETE), // SPACE=(TRK,(0,0)) //* //BLQML49A DD DSN=IBMUSER.DHB.BLQML49A, // DISP=(MOD,DELETE), // SPACE=(TRK,(0,0))
下面的例子建立(Create)两个QSAM文件,供后面的作业步使用。
//DFQSAM1 EXEC PGM=IEFBR14 //CRTFIL1 DD DSN=IBMUSER.OTB.BLQOVBLI.MAC, // DISP=(NEW,CATLG), // UNIT=SYSDA, // SPACE=(TRK,(10,5),RLSE), // DCB=(RECFM=FB,LRECL=208,BLKSIZE=208) //CRTFIL2 DD DSN=IBMUSER.OTB.BLQOVBLO.MAC, // DISP=(NEW,CATLG), // UNIT=SYSDA, // SPACE=(TRK,(10,5),RLSE), // DCB=(RECFM=FB,LRECL=208,BLKSIZE=208) //*
2.13.16 文件的成批复制和重命名
DFDSS(Data Facility Data Set Services)是一个磁盘管理工具,它可用于移动、备份和恢复、复制数据。它可用JCL、ISMF及应用程序调用。ADRDSSU是DFSMS/DSS工具中的一员,是DASD数据和空间管理工具,常用来对数据集做备份和恢复。下面的例子将一个卷(SITD01)上以SPF开头的所有数据集搬(Move)新磁盘卷SITD03上。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //DFDCOPY EXEC PGM=ADRDSSU,REGION=4096K 000004 //DASD1 DD VOL=SER=SITD01,UNIT=DASD,DISP=SHR 000005 //DASD2 DD VOL=SER=SITD03,UNIT=DASD,DISP=SHR 000006 //SYSPRINT DD SYSOUT=* 000007 //SYSIN DD * 000008 COPY LOGINDD(DASD1) - 000009 OUTDD(DASD2) - 000010 DATASET( INCLUDE ( - 000011 SPF.**, - 000012 )) - 000013 ALLDATA(*) - 000014 ALLEXCP - 000015 TOL(ENQF) CATALOG DELETE PURGE 000016 /*
控制语句的说明如下。
● COPY表明所做的工作是复制,是DFDSS一条命令,表示做复制操作。
● LOGINDD指定输入卷名。表示源卷,逻辑复制。所谓逻辑复制指以磁道为单位进行复制,物理复制则不以磁道为单位。如果是物理复制则用INDDNAME。
● OUTDD指定输出卷名,即表示目标卷。
● DATASET指定要复制的数据集,括号内的INCLUDE是过滤器。
● INCLUDE指定要复制的文件名字
● ALLDATA(*)复制所有文件已分配的空间,即文件不可以为空。
● ALLEXCP:复制所有已分配的空间,文件可以为空,一般和ALLDATA互相作用,组合使用。
● TOLERATE(ENQF)使要处理的数据集不适用也可以复制,与参数FULL、TRACKS冲突。
● DELETE表示复制结束后删除源。
● CATALOG表示复制结束后对目标进行编目。
在过滤器中,*代表一个分段通配符,%代表一个字符通配符,INCLUDE表示所有数据集,EXCLUDE表示除去这些以外的所有数据集。也可用BY过滤,其格式为:BY(KEY,OP,ARG)。
其中KEY是:
● ALLOC:建立数据集时分配的类型(柱面、磁道、块)。
● CATLG:数据集是否编目。
● CREDT:数据集的创建时间。
● DACHA:数据集上次备份后是否改变。
● DSORG:数据集的组织结构(SAM、PDS、PDSE、HFSH或VSAM等)。
● EXPDT:数据集的到期时间。
● REFDT:数据集的上次访问时间。
● DATACLAS:创建数据集时使用的Data Class。
● STORCLAS:创建数据集时使用的Storage Class。
● MGMTCLAS:创建数据集时使用的Management Class。
OP可以是:
● EQ:等于。
● LE:小于或等于。
● LT:小于。
● GT:大于。
● GE:大于或等于。
● NE:不等于。
下面的例子除了复制文件外,还将复制后的文件改名:
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //********************************************** 000004 //STEP01 EXEC PGM=ADRDSSU,REGION=6M 000005 //********************************************** 000006 //SYSPRINT DD SYSOUT=* 000007 //SYSIN DD * 000008 COPY DATASET(INCLUDE( - 000009 IBMUSER.DHB.BLQOVBLO.HK - 000010 IBMUSER.DHB.BLQOVBLO.SHN - 000011 ) ) - 000012 RENAMEU( - 000013 (IBMUSER.DHB.BLQOVBLO.HK,IBMUSER.DHB.BLQOVBLI.HK) - 000014 (IBMUSER.DHB.BLQOVBLO.SHN,IBMUSER.DHB.BLQOVBLI.SHN) - 000015 ) - 000016 CATALOG TOL (ENQF) 000017 //
控制语句的说明如下。
● RENAMEU表示在复制完后,将文件重命名,比如,上面二个以HK结尾的文件会改成以SHN结尾,而名字前面的部分不变。
2.13.17 分类
数据集包含需要排序、合并、复制的信息。例程ICEMAN和ICETOOL完成的大多数处理都是对整个数据集排序的。已编目的数据集的使用是通过它的名字来访问的,不需要指定它的存储位置(卷名)。编目数据集不应同编目过程混淆在一起,编目过程是存储在数据集里的JCL集合,你帮它取一个名字而已,而编目数据集是一个名字记录在系统目录中的数据集。
ICEMAN功能与ICETOOL类似,但在一个作业步里只能操作一对数据集、进行一次分类,而ICETOOL则可以进行多次。另外,ICEMAN还可以通过MERGE语句将不同数据集的数据合并到一个数据集中。ICEMAN的输入输出数据集分别用SORTIN、SORTOUT标识,控制数据集为SYSIN。
2.13.17.1 数据集的排序
排序是将记录按升序或降序的顺序写到输出文件的过程,在JCL EXEC语句中执行DFSORT,编写一个SORT控制语句去描述控制区域及将要排序的顺序。控制语句是输入流的一部分,输入流由JCL里的SYSIN DD语句读入。分类SORT控制语句的格式为:
其中,可以用来做数据分类的数据类型如表2.4 所示,其中,字符(CH)、数字(ZD)、压缩十进制数(ZD)和无符号二进制数(BI)是我们常用的数据类型。
表2.4 分类的数据类型
记录可以通过指定多个控制字段进行排序,但同时需指定从最大到最小优先级的顺序。比如,下面指定了3个要排序的字段,它们的优先级与它们出现在SORT控制语句的先后顺序是一致的,即排在最前面的排序时的优先级最高,而最后的优先级最低,其他依次类推。
分类也可以从COBOL程序中调用,但我们不建议这样做,因为将分类有作业流来完成,对于提高系统维护是有很多好处的。详细的情况就不在此解释了。
下面是一个分类的例子。
000001 //IBMUSERA JOB CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1),NOTIFY=&SYSUID, 000002 // REGION=4M 000003 //SRTRAN EXEC PGM=ICEMAN 000004 //SYSOUT DD SYSOUT=* 000005 //SORTIN DD DSN=IBMUSER.TEST.BKTRAN,DISP=SHR 000006 //SORTOUT DD DSN=IBMUSER.TEST.BKTRAN.SORT, 000007 // DISP=(NEW,CATLG), 000008 // SPACE=(TRK,(5,5),RLSE), 000009 // DCB=(RECFM=FB,LRECL=80,DSORG=PS) 000010 //SYSIN DD * 000011 SORT FIELDS=(8,14,CH,A,1,4,CH,A) 000012 /*
控制语句的说明如下。
● 分类所执行的程序为ICEMAN。
● SORTIN指定分类的输入数据集,这里是我们在后面用到的银行主文件更新程序的交易文件,其记录格式你可以在本书的COBOL部分找到。
● SORTOUT指定分类后的输出数据集,即是排好序的数据集。
● SYSOUT指定作业输出清单要去的打印机,这里指定SYSOUT=*表示输出清单会与作业流清单一起。
● SYSIN指定控制语句所在的数据集,大多数时候会是一个参数(PARAMETER)数据集,我们这里使用的是内部数据流,即控制语句直接写在作业流中。
● 控制语句SORT FIELDS指定要排序的字段,我们可以看到,这里的排序使用了两个字段。
✧ 一个是银行账号(从记录的第8位开始,账号字段的长度是14,类型为字符型,按照上升的顺序排序)。
✧ 另外一个排序的字段为交易日志号,它是按照系统写记录的先后顺序编写的,即日志号较小的先写。日志号从记录的第1位开始,长度为4,数据类型为数字的(显示型的数字跟字符编码是一样的,所以我们仍然当成字符处理),也是按日志号的升序排列的。
✧ 如果只指定一个账号作为分类键,会产生的问题是:当账号相同时,系统并不保证它们的先后顺序。换句话说,对同一个账号来说,可能会将后做的交易排在先做的交易前面,这是我们处理时所不能容忍的,所以,为了保证先做的交易出现在后做交易的前面,我们必须增加日志号作为分类的第2个字段,以保证同账号的交易,日志号小的一定排在日志号大的前面。
2.13.17.2 数据集的合并
一般来说,合并数据集的原因是要增加多条记录到一个已排序的数据集里,通过编写MERGE控制语句和几个JCL语句来完成合并数据集的任务。无论何时合并数据集,都必须确认它们的记录具有相同格式,并由同一个控制字段先排序。每次,最多可以合并16个数据集。合并数据集的控制语句为:
MERGE语句的格式类似SORT语句,不同的是增加了短语FORMAT=,它用来说明分类的输入字段的数据类型。此外,用SORTINnn DD语句取代SORTIN DD语句来定义输入数据集。SORTINnn DD语句命名输入数据集,并告知有多少数据集被合并,每一个合并的数据集都需要一个SORTINnn DD语句,SORTINnn中nn的值从01到16。
下面是一个数据集合并的例子。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //SRTRAN EXEC PGM=ICEMAN 000004 //SYSOUT DD SYSOUT=* 000005 //SORTIN01 DD DSN=IBMUSER.TEST.BKTRAN.DEPOSIT,DISP=SHR 000006 //SORTIN02 DD DSN=IBMUSER.TEST.BKTRAN.LOANS,DISP=SHR 000007 //SORTIN03 DD DSN=IBMUSER.TEST.BKTRAN.CREDIT,DISP=SHR 000008 //SORTOUT DD DSN=IBMUSER.TEST.BKTRAN.MERGE, 000009 // DISP=(NEW,CATLG), 000010 // SPACE=(TRK,(5,5),RLSE), 000011 // DCB=(RECFM=FB,LRECL=80,DSORG=PS) 000012 //SYSIN DD * 000013 MERGE FIELDS=(8,14,CH,A),FILES=3 000014 RECORD TYPE=F 000015 END 000016 /*
控制语句说明如下。
● SORTIN01到SORTIN03指定合并的输入文件,它们必须是语句排好序的。
● MERGE指定要做的是合并操作。
● FIELDS指定要合并的字段,注意必须要跟输入文件原来的排序字段一致,否则系统会出现顺序出错(OUT OF SERQUENCE)的错误。
● FILES指定要合并的输入文件的个数。
2.13.17.3 数据集的复制
用DFSORT可以直接复制数据集,而不需要运行排序或合并。在SORT、MERGE中指定COPY语句,或指定SYSIN DD语句的OPTION控制语句来完成复制。
SORT FIELDS=COPY MERGE FIELDS=COPY OPTION COPY
下面是一个数据集复制的例子,DD语句SORTIN中指定的文件原封不动地复制到了DD语句SORTOUT定义的文件中。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //SRTRAN EXEC PGM=ICEMAN 000004 //SYSOUT DD SYSOUT=* 000005 //SORTIN DD DSN=IBMUSER.TEST.BKTRAN,DISP=SHR 000006 //SORTOUT DD DSN=IBMUSER.TEST.BKTRAN.BKUP, 000007 // DISP=(NEW,CATLG), 000008 // SPACE=(TRK,(5,5),RLSE), 000009 // DCB=(RECFM=FB,LRECL=80,DSORG=PS) 000010 //SYSIN DD * 000011 SORT FIELDS=COPY 000012 RECORD TYPE=F,LENGTH=80 000013 /*
控制语句说明如下。
● SORTIN指定要复制的输入文件。
● SORTOUT指定复制的输出文件。
● SORT FIELDS=COPY说明要做复制操作,不需要排序。
● RECORD指定处理文件的类型(F表示定长记录)和记录长度。
2.13.17.4 数据集记录的筛选——包含满足条件的记录
ICETOOL是DFSORT(ICEMAN)的扩充,主要用于对数据进行排序、从中抽取需要的数据项,或者过滤掉某些不符合条件的记录等。下例将对源数据集中的记录先按账号的升序,再按日志号降序排序,并过滤掉账号小于等于“01228863000034”的记录,最后输出这3项数据(账号、日志和交易码)到目标数据集中。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //SORTTRN EXEC PGM=ICETOOL,COND=(4,LT) 000004 //TOOLMSG DD SYSOUT=* 000005 //DFSMSG DD SYSOUT=* 000006 //SYSOUT DD SYSOUT=* 000007 //INFILE DD DSN=IBMUSER.TEST.BKTRAN,DISP=SHR 000008 //OUTFILE DD DSN=IBMUSER.TEST.BKTRAN.INCLUDE, 000009 // DISP=(NEW,CATLG), 000010 // SPACE=(TRK,(5,5),RLSE), 000011 // DCB=(RECFM=FB,LRECL=21,DSORG=PS) 000012 //TOOLIN DD * 000013 SORT FROM(INFILE) TO(OUTFILE) USING(SRT1) 000014 //* 000015 //SRT1CNTL DD * 000016 SORT FIELDS=(8,14,CH,A,1,4,CH,D) 000017 INCLUDE COND=(8,14,CH,GT,C'01287500000034') 000018 OUTREC FIELDS=(8,14,1,4,5,3) 000019 //*
控制语句说明如下。
● 执行的程序是ICETOOL,它扩充了ICEMAN的功能。
● INFILE指定筛选前的输入文件。
● OUTFILE指定筛选后的输出文件。
● TOOLIN指定ICETOOL使用的控制语句,其格式如下。
SORT FROM(indd) TO(outdd) USING(xxxx),其中:
✧ FROM(indd)
指定输入数据集,它是DFSORT例程的输入文件,必须指定至少一个indd DD语句,而且定义的文件必须满足DFSORT对SORTIN数据集的要求。
✧ USING(xxxx)
指定DFSORT所要使用的控制语句数据集的前4个字符。xxxx必须是合法的上个字符,并与CNTL一起组成合法的DD名xxxxCNTL。DD语句xxxxCNTL必须存在,而且该文件中的控制语句必须遵守DFSORT例程关于SORTCNTL控制语句的规定。xxxxCNTL数据集必须包含SORT语句。如果没有指定TO短语,xxxxCNTL数据集就必须包含一个或多个OUTFIL语句或MODS语句。其他语句是可选的。
✧ TO(outdd,...)
指定输出数据集的DD名字,最多可以指定10个输出数据集。作业流中对每个outdd必须有一个对应的DD语句说明。如果只指定单个outdd数据集,系统就只调用一次DFSORT例程,使用SORTOUT处理将输入数据集中的内容分类到输出数据集中。outdd对应的数据集必须满足DFSORT对SORTOUT数据集的要求。如果指定多个outdd数据集,并且没有指定SERIAL短语,系统仍然调用一次DFSORT,将输入数据集的内容排序后输出到outdd数据集中,但这次使用OUTFIL处理流程。outdd数据集必须满足DFSORT对OUTFIL数据集的要求。在FROM短语中指定的ddname不能同时指定到TO参数中作为输出文件。
● SORT1CNTL指定分类控制语句,其中的语句有:
✧ SORT FIELDS
指定分类的字段,这里我们指定了两个分类字段,一个是账号,另一个是日志号。
✧ INCLUDE COND
指定要输出到输出文件中的记录要满足的条件。在这个例子中,只有账号大于01287500000034 的记录才会写到输出文件中。
✧ OUTREC FIELDS
指定需要输出到输出文件中的字段。在我们的例子中,输出到输出文件中的指定字段有3个,它们是账户(从输入记录的第8列开始,长度为14)、日志号(从输入记录的第1列开始,长度为4)和交易码(从输入记录的第5列开始,长度为3)。所以,输出文件的记录格式就是账号+日志号+交易码,总长度为21(14+4+3)。
2.13.17.5 数据集记录的筛选——生成头记录
下面的例子根据输入文件产生只有一条记录的批处理控制文件。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*--------------------------------------------------------------* 000004 //* CREATE DUMMY HEADER IN BKTRAN.HEADER FILE * 000005 //*--------------------------------------------------------------* 000006 //STEP2 EXEC PGM=ICETOOL 000007 //TOOLMSG DD SYSOUT=* 000008 //DFSMSG DD SYSOUT=* 000009 //TOOLIN DD * 000010 SORT FROM(INDS1) TO(OUTDS1) USING(C000) 000011 //INDS1 DD DSN=IBMUSER.TEST.BKTRAN,DISP=SHR 000012 //OUTDS1 DD DSN=IBMUSER.TEST.BKTRAN.HEADER, 000013 // DISP=(NEW,CATLG), 000014 // SPACE=(TRK,(1,1),RLSE), 000015 // DCB=(RECFM=FB,LRECL=12,DSORG=PS) 000016 //C000CNTL DD * 000017 RECORD TYPE=F 000018 OPTION COPY,STOPAFT=1 000019 OUTREC FIELDS=(C'6BATCH',3X,8,3) 000020 /*
控制语句说明如下。
● 控制语句OPTION COPY表示要从输入文件复制到输出文件中。
● STOPAFT指定要复制的最大记录数。在我们的例子中,由于指定SOTPAFT为1,因此只会复制一条记录到输出文件中。
● 其他语句与上一个例子类似,这里就不重复了。
● 输出文件产生的记录为:6BATCH 012,其中012是账号的前3位,即银行的行号。
2.13.17.6 数据集记录的筛选——跳过第1条记录
下面的例子复制输入文件到输出文件中,跳过第1条记录,然后按照日志号排序。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*--------------------------------------------------------------* 000004 //* SKIP REORD 1 FROM INPUT FILE * 000005 //*--------------------------------------------------------------* 000006 //SKIPREC1 EXEC PGM=ICETOOL 000007 //TOOLMSG DD SYSOUT=* 000008 //DFSMSG DD SYSOUT=* 000009 //TOOLIN DD * 000010 SORT FROM(INDS1) TO(OUTDS1) USING(C000) 000011 //INDS1 DD DSN=IBMUSER.TEST.BKTRAN,DISP=SHR 000012 //OUTDS1 DD DSN=IBMUSER.TEST.BKTRAN.SKIP, 000013 // DISP=(NEW,CATLG), 000014 // SPACE=(TRK,(1,1),RLSE), 000015 // DCB=(RECFM=FB,LRECL=21,DSORG=PS) 000016 //C000CNTL DD * 000017 OPTION SKIPREC=1 000018 OUTREC FIELDS=(8,14,1,4,5,3) 000019 SORT FIELDS=(1,4,CH,A) 000020 /*
控制语句说明如下。
● 控制语句OPTION SKIPREC=1表示要跳过输入文件的第1条记录,再复制到输出文件中。
● 输出文件的记录格式就是账号+日志号+交易码,总长度为21(14+4+3)。
● 其他语句与上一个例子类似,这里就不重复了。
2.13.17.7 交易合计
下面的例子对同账号的交易代码为‘DEP’或‘WIT’的记录求和,结果存放到SORTOUT中。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*--------------------------------------------------------------* 000004 //* SUM TRANSACTION TOTAL FOR SAME ACCOUNT WITH DEP/WIT TX TYPES * 000005 //*--------------------------------------------------------------* 000006 //SORT001 EXEC PGM=ICEMAN,REGION=3M 000007 //SORTIN DD DSN=IBMUSER.TEST.BKTRAN,DISP=SHR 000008 //SORTOUT DD DSN=IBMUSER.TEST.BKTRAN.SUM, 000009 // DISP=(NEW,CATLG), 000010 // SPACE=(TRK,(1,1),RLSE), 000011 // DCB=(RECFM=FB,LRECL=80,DSORG=PS) 000012 //SYSOUT DD SYSOUT=* 000013 //SORT001 DD UNIT=SYSDA,SPACE=(CYL,5) 000014 //SYSIN DD * 000015 SORT FIELDS=(8,14,CH,A) 000016 SUM FIELDS=(54,15),FORMAT=ZD 000017 INCLUDE COND=(5,3,CH,EQ,C'DEP',OR, 000018 5,3,CH,EQ,C'WIT') 000019 /*
控制语句说明如下。
● SORT FIELDS语句指定按账户的上升顺序排序。
● SUM FIELDS语句指定按交易金额(起始位置54,长度为15)来求和。当碰到指定在SORT语句中的控制字段(即账户)相同时,在SUM语句中指定的字段(即交易金额)就会加起来,并放到其中一条记录中,而另外一条记录就会从输出文件中删除掉。换句话说,同账号的记录在输出文件中只有一条。
● 控制语句INCLUDE指定要包含到输出文件中的记录的条件。在我们的交易文件中,一共有两类交易,一类是账户资料类,另一类是金融交易类。两类交易的格式是不同的。账户资料类交易没有金额,而只有金融交易类才有金额。因此,合计只能对金融类交易而言。如果对账户资料类交易求和,就会出现系统错误S0C7,即数据例外(data exception)的错误。
● 其他语句与上一个例子类似,这里就不重复了。
2.13.17.8 将输入文件分成两个文件
下面的例子讲我们的交易文件按照交易类型分成两个文件,其中,一个文件的内容包含所有客户资料类交易,它们的交易码为OPN、UPD和CLS;另一类文件包含金融类交易,它们的交易码为DEP和WIT。
000001 //IBMUSERA JOB 168,'NEWMAN LV',CLASS=A,MSGCLASS=A,MSGLEVEL=(1,1), 000002 // NOTIFY=IBMUSER 000003 //*--------------------------------------------------------------* 000004 //* SPLIT FILE INTO 2 FILES * 000005 //*--------------------------------------------------------------* 000006 //SORT001 EXEC PGM=ICEMAN,REGION=3M 000007 //SORTIN DD DSN=IBMUSER.TEST.BKTRAN.KSDS.CLUSTER,DISP=SHR 000008 //SORTOF01 DD DSN=IBMUSER.TEST.BKTRAN.INFO, 000009 // DISP=(NEW,CATLG), 000010 // SPACE=(TRK,(1,1),RLSE), 000011 // DCB=(RECFM=FB,LRECL=80,DSORG=PS) 000012 //SORTOF02 DD DSN=IBMUSER.TEST.BKTRAN.FINS, 000013 // DISP=(NEW,CATLG), 000014 // SPACE=(TRK,(1,1),RLSE), 000015 // DCB=(RECFM=FB,LRECL=80,DSORG=PS) 000016 //SYSOUT DD SYSOUT=* 000017 //SORT001 DD UNIT=SYSDA,SPACE=(CYL,5) 000018 //SYSIN DD * 000019 SORT FIELDS=COPY 000020 OUTFIL FILES=01,INCLUDE=(5,3,CH,EQ,C'OPN',OR, 000021 5,3,CH,EQ,C'UPD',OR, 000022 5,3,CH,EQ,C'CLS') 000023 OUTFIL FILES=02,INCLUDE=(5,3,CH,EQ,C'DEP',OR, 000024 5,3,CH,EQ,C'WIT') 000025 /*
控制语句说明如下。
● SORT FIELDS=COPY表示要复制文件。
● OUTFIL指定输出文件,INCLUDE指定要输出到输出文件中的记录类型,FILES=01表示要输出到第1个文件,即有SORTOF01定义的文件中,类似地,FILE=02表示要输出到由SORTOF02定义的文件中。
● 运行的结果将输入文件按照交易类别分成了两个文件。