CMS/TSO 管道(PipeLines)Windows版 - 第一部分 语法
IBM 管道(Windows 版)和 PowerShell 的实现。
引言
CMS/TSO PipeLines 是一个运行在IBM大型机上的产品。在z/VM系统上,它在CMS下运行;在z/OS系统上,它在TSO下运行。顾名思义,数据通过一系列内置或用户定义的阶段进行传递。在此过程中,数据可以被修改或从管道中删除。你可能会说,这没什么新颖的。不同之处在于,PipeLines支持并发并行管道,并且数据可以被路由进出管道。
系统要求
虽然PipeLib类库可以从.Net程序访问,但我认为在PowerShell下运行它会更有用。我所有的测试都是通过PowerShell进行的,任何我提供的管道最有可能都是PowerShell脚本。因此,我建议的最低系统要求如下。
PowerShell V1.0
.Net Framework V2
语法
首先,我想描述一下在IBM手册和本产品中使用的语法图。语法图的基础是命令行。命令行的元素是
>>—————>< | 命令行以>>开头,以><结尾。 |
—————> | 这表示语法在下方继续。 |
>————— | 这表示这是从上方继续的。 |
>>——Command——required option———————————><
如果项目堆叠在一个必需选项的下方,这表示必须选择其中一个项目。>>——Command——┬option1───┬———————><
├option2───┤
└option3───┘
如果一个项目出现在命令行下方,那么它是可选的,可以不提供。>>——Command——┬—————————─┬———————><
├option1───┤
└option2───┘
如果一个项目出现在命令行上方,那么如果没有提供选项,它就是默认值。 ┌default—──┐
>>——Command——┼─────————─┼———————><
├option1───┤
└option2───┘
一个选项上方的弧线表示它可以被输入多次,或者可以选择组中的多个选项。下方的弧线表示你可以输入option1和/或option2,如果两者都没有提供,则假定为默认值。如果选项之间需要分隔符,则在弧线上的<之后指定。 ┌─<────────┐
│┌default—┐│
>>——Command—┴┼────————┼┴——————><
├option1─┤
└option2─┘
语法片段或组用于当语法的某一部分重复或递归时。它也可以用来简化命令的主行,并将其分解为单独的命令。>>——Command—┤segment├———><
Group segment
├───segment options────┤
完全用小写字母表示的选项表示用户提供的变量。如果选项完全用大写字母表示或以大写字母开头,那么它就是一个关键字。大写字母表示关键字的最小缩写。>>——Command——KEYword——variable——————><
在上例中,关键字可以只输入"KEY",而"variable"则是用户提供的数据。让我们来看一个实际的例子。下面是SQL内置阶段的语法图。
┌─<───────────────┐
│┌NOPAD COLSEP ~─┐│
>>─Sql─┴┼───────────────┼┴┬─────────┬─><
├┬PAD───┬───────┤ └┤connect├┘
│└NOPAD─┘ │
└COLSEP xorc────┘
Group Connect
(1) ┌─<─;──────┐
├─CONNect──┬SQL────┬┴key=value─┴─┤
├ODBC───┤
├OLE────┤
└ORACLE─┘
Group Request
├─┬────────────────────┬┬┬─────────┬SELECT─select statement─┬─┤
│ ┌─<────────────┐ ││└DESCribe─┘ │
└(─┴┬────────────┬┴)─┘└DESCribe─tablename─────────────────┘
├┬PAD───────┬┤
│└NOPAD─────┘│
└COLSEP xorc─┘
Notes
1. When a Connect statement is specified on a Sql stage specification,
omit the "Connect" keyword.
关注点- 出现在命令行或下方以及选项行上的规则也适用。请注意"PAD"、"NOPAD"选项,两者只能选择其一,不能同时选择。如果你省略这些选项,则假定默认值为"NOPAD COLSEP ~"。
- 你可以选择在阶段规范中提供一个“Connect”语句。如果这样做,根据注释,你将省略“CONNECT”关键字。
- 在“Connect”语句中,你选择连接类型,并指定由分号分隔的定义连接的“key=value”对。
- “Group Request”不是SQL阶段语法的一部分。它描述了阶段读取的输入记录。在Request中,你可以覆盖阶段规范中指定的选项,并且选项必须用括号括起来。你可以指定一个SQL select语句,可以选择在前面加上“DESCRIBE”关键字。或者,你可以指定“DESCRIBE”关键字后跟一个表名。
管道定义语法
下面是管道定义的语法图。通过后面的示例进一步解释了语法。
┌─<─endchar┐
>>──┬─────────────────────────┬┬─────────┬┴┤PipeLine├┴─><
│ ┌─<─────────────────┐ │├stagesep─┤
└(─┴┬┬STAGESEP──┬xorc─┬┴)─┘└endchar──┘
│└SEPerator─┘ │
├ENDchar xorc─────┤
├ESCape xorc──────┤
├NAME─pipename────┤
└TRACE────────────┘
Group PipeLine
┌─<─stagesep───────────────────────────────────────────────┐
├─┴┬────────────────────────────────────────────────────────┬┴─┤
├┬────────────┬┬───────────────┬stagename─┬─────────────┬┤
│└┤LabelGroup├┘└(─┬───┬TRACE─)─┘ └stageoptions─┘│
│ └NO─┘ │
└┤LabelGroup├────────────────────────────────────────────┘
Group LabelGroup
├─┬────────────────┬─┤
├label:──────────┤
└label.streamid:─┘
- 选项
- STAGESEP xorc
分隔符 - 指定用于分隔管道阶段的字符。它可以指定为单个字符或字符的十六进制数字。默认分隔符是竖线“|”。
- ENDchar xorc
- 指定分隔管道中各个管道的字符。它可以指定为单个字符或字符的十六进制数字。没有默认值。
- ESCape xorc
- 定义一个转义字符,用于覆盖PipeLine解析器具有特殊含义的字符的处理。任何跟在转义字符后面的字符都将被视为普通字符。没有默认值。
- NAME pipename
- 指定与此管道关联的名称。
- TRACE
- 开启管道的跟踪。
- 管道
- stagesep
- 管道的阶段由STAGESEP字符分隔。默认值为“|”。
- endchar
- 如果管道中有多个管道,则用ENDCHAR分隔它们。
- 否
- 如果管道已开启跟踪,则“NO TRACE”将关闭此管道阶段的跟踪。
- TRACE
- 为此管道的阶段开启跟踪。如果以“NO”关键字为前缀,则关闭此阶段的跟踪。
- stagename
- 要执行的阶段的名称。
- stageoptions
- 提供指定阶段所需的任何选项。
- label
label.streamid - 一个标签,用于标识阶段的其他输入和/或输出数据流。标签的第一次出现称为标签定义。它建立了一个连接点,其他管道中的阶段可以从中接收数据或向其发送数据。每次后续使用相同的标签称为标签引用。使用标签引用来定义阶段的其他输入和输出流。要使用标签引用,请指定一个只包含标签而没有阶段名称的阶段。
streamid为流分配一个符号名称。通过向标签添加标识符来命名流。将标签紧跟一个点(.)和一个最多4个字母数字字符的组合(该组合必须至少包含一个字母字符),或者一个字母数字字符的组合(该组合必须至少包含一个字母字符)。流标识符必须紧跟一个冒号,中间没有空格。
示例管道
最简单的管道形式包括一系列由STAGESEP字符分隔的阶段定义。
stage1 |stage2 |... |stagen下一个复杂级别是定义一个管道中的多个管道。
(end ?) stage1-p1 |... |stagen-p1 ? stage1-p2 |stage2-p2 |... |stagen-p2这里我们指定一个“ENDCHAR”来分隔管道定义中的各个管道。
但是,只有在使用流标签时,管道的全部功能才能实现。
关于数据流的说明。每个管道中的每个阶段默认有两个流:主输入和输出流。主输入从管道中的前一个阶段读取数据,主输出写入到管道中的下一个阶段。仅仅定义一个流并不意味着它已连接到任何东西。例如,管道中的第一个阶段有一个主输入,但它未连接,因为没有前一个阶段可以连接。同样,管道中的最后一个阶段有一个主输出,但它未连接。
定义流标签的阶段建立了一个用于额外输入或输出流的连接点。流标签可以在管道中的三个位置之一被引用。
- 管道的第一个阶段。
管道第一个阶段引用的流标签表示在定义阶段创建了一个输出流。定义该标签的阶段将数据写入引用标签后面的阶段。 - 在管道中间。
管道中间引用的流标签在定义阶段创建输入和输出流。来自引用标签前一个阶段的数据被写入定义流标签的阶段。然后,定义阶段将数据写入引用标签后面的阶段。 - 管道的最后一个阶段。
最后一个阶段引用的流标签表示在定义阶段创建了一个输入数据流。来自引用标签前一个阶段的数据被写入定义阶段。
名称 | 数字 |
---|---|
主(Primary) | 0 |
次级 | 1 |
第三级 | 2 |
第四级 | 3 |
第五级 | 4 |
第六级 | 5 |
考虑以下管道段
- '(end ?) < file.txt'
- '|l:find abc'
- '|...'
- '|f:faninany'
- '|console'
- '? l:'
- '|...'
- '|f:'
- 定义ENDCHAR并读取文件“file.txt”的内容。
- 定义流标签l:并执行find阶段。
此阶段查找字符串abc,从记录的第1列开始。如果找到字符串,则将记录传递给管道中的下一阶段。如果未找到字符串,则记录将被写入由l:定义的數據流,如果已连接。 - 处理包含字符串abc(从第1列开始)的记录。
- 定义流标签f:并执行faninany阶段。此阶段读取管道中前一个阶段和任何已连接数据流的记录。
- 执行console阶段。顾名思义,它将数据写入终端屏幕。
- 我们启动一个新的管道并引用流标签l:。由于流标签出现在管道定义的开头,它为第2行中的find阶段创建了次级输出流。未被find阶段选中的记录将进入这里。
如果稍后在管道中再次引用l:标签,则会为find阶段创建第三级输出流。或者,根据引用发生的位置,可以为find阶段创建一个次级输入数据流。但是,find阶段只支持次级输出流。 - 处理记录。
- 我们引用流标签f:。由于它位于管道定义的末尾,因此它在定义阶段创建了一个次级输入数据流。它将来自前一个阶段的数据发送到定义了流标签的阶段。在本例中是第4行中的faninany阶段。与find阶段不同,faninany阶段支持多个输入流。如果稍后在管道中再次引用f:标签,我们将创建第三级输入流。
实际案例
此阶段会创建一个内置阶段的HTML表,如果该阶段有文档,则会创建一个链接。请注意,下面的定义看起来就像它在PowerShell脚本中一样。
- '(end ?) shell ls ..\..\runtime\* -include *.cs -name '+
- '-exclude util.cs,stageprocessor.cs,dispatcher.cs,addcallstream.cs,spec*,codepages.cs,'+
- 'old*,parsercntrl.cs,structCntrl.cs'+
- '|nlocate /File.cs/'+
- '|chop .'+
- '|m:faninany'+
- '|sort'+
- '|l:lookup detail'+
- '|spec $<a href="Doc/$ 1 1-* n $.html">$ n 1-* n $</a>$ n'+
- '|f:faninany'+
- '|literal <a href="Doc/AppendFile.html">>></a>'+
- '|literal <a href="Doc/CreateFile.html">></a>'+
- '|literal <a href="Doc/ReadFile.html"><</a>'+
- '|spec $<td>$ 1 1-* n $</td>$ n'+
- '|join 7'+
- '|spec $<tr>$ 1 1-* n $</tr>$ n'+
- '|literal <table cellspacing="5" cellpadding="5">'+
- '|literal append +</table>+'+
- '|> StageTable.html'+
- '? literal Hostbyaddr Hostbyname NFind StrFind StrFrLabel StrLiteral StrNFind Spec StrToLabel StrWhileLabel'+
- '|split'+
- '|m:'+
- '? shell ls ..\..\rrt\* -include *.html -name '+
- '|c:fanout'+
- '|chop .'+
- '|l:'+
- '|hby:nfind Hostby'+
- '|str:nfind any /str/'+
- '|nf:nfind NFind'+
- '|f:'+
- '? hby:'+
- '|spec $<a href="Doc/GetHost.html">$ 1 1-* n $</a>$ n'+
- '|f:'+
- '? str:'+
- '|if1:if all ^/Stru/ & ^/Strip/'+
- '|if2:if find any /strnfind/'+
- '|spec $<a href="Doc/Find.html">$ 1 1-* n $</a>$ n'+
- '|if2:'+
- '|spec $<a href="Doc/$ 1 4-* n $.html">$ n 1-* n $</a>$ n'+
- '|if2:'+
- '|if1:'+
- '|f:'+
- '? nf:'+
- '|spec $<a href="Doc/Find.html">$ 1 1-* n $</a>$ n'+
- '|f:'+
- '? c:'+
- '|spec $copy ..\..\rrt\$ 1 1-* n $Doc\$ nw'+
- '|shell'+
- '|cons'
- shell阶段执行PowerShell命令,本例中是ls。它将列出所有内置阶段的名称,排除那些不是阶段的名称。
- 第1行上方的一部分。
- 第1行上方的一部分。
- nlocate阶段查找字符串File.cs。如果找到字符串,它将丢弃该记录。所有其他记录将被传递到下一阶段。
- chop查找记录中的句点。如果找到,它将在该处分割记录。记录中句点之前的部分将被写入下一阶段。句点之后的部分将被丢弃。
- 在这里,我们合并了阶段名称的别名和其他阶段名称,以保持与IBM的源兼容性。faninany阶段将在数据可用时从任何已连接的流读取数据。
- 按字母顺序对阶段名称进行排序。由于我们没有提供任何键范围,整个记录被用作排序键。
- lookup阶段将详细记录与一组主记录进行比较。详细记录从Primary输入流读取。主记录从Secondary输入流读取,该流在第26行创建。与主记录匹配的详细记录被写入Primary输出流。没有匹配主记录的记录被写入Secondary输出流,该流也在第26行创建。写入Primary输出流的任何记录都有一个文档网页。
- spec阶段用于修改现有记录和/或将新记录插入数据流。在本例中,我们正在创建指向当前输入记录中的阶段名称的文档页面的链接。我们首先在输出记录的第1列插入<a href="Doc/GetHost.html">。然后,我们将当前记录在Primary输入流中的内容插入到输出记录的下一列,后面跟着字符串</a>。然后将此新记录写入Primary输出。
- 请记住第8行的lookup阶段。它写入的记录是Secondary输出,那些没有匹配主记录的记录将在此处的Secondary输入流中结束。faninany阶段将从任何有数据准备读取的输入流中读取一条记录。
- 在第4行,我们丢弃了任何包含字符串“File.cs”的记录。在这里,我们将它们添加回来,并链接到已知存在的文档。
- 参见第11行。
- 参见第11行。
- 此spec阶段创建一个HTML表单元格。
- join阶段读取一条记录,并将其与接下来的7条记录连接起来。然后它写入一条包含8个单元格定义的记录。
- 此spec阶段从8个单元格定义创建一个表行。
- 默认情况下,literal阶段在读取和写入其Primary输入流中的记录之前,将其文字字符串写入其主输出。因此,表标签将是输出文件中的第一条记录。
- 这个literal阶段被指示在读取并写入其Primary输入流中的所有数据之后,写入其文字字符串。因此,结束的表标签将是输出文件中的最后一条记录。
- >阶段,它是“CreateFile”的别名,打开指定的ファイル并将其Primary输入流中的数据写入该文件。如果文件已存在,则会被覆盖,否则会创建一个新文件。这是管道中第一个管道的最后一个阶段。
- 这是第二个管道的第一个阶段。这个literal阶段插入了一个包含上面省略的别名和其他阶段名称的字符串。
- split阶段将记录分解为段,并将每个段写入其Primary输出。在这种情况下,断点是任何空格字符。
- 这个引用标签m:将要合并的阶段名称发送到第6行的主要数据流。
- 这是第三个管道的第一个阶段,它提取所有文档网页的名称。
- fanout阶段将每个输入记录的副本写入所有连接的输出流。在本例中是Primary和Secondary流。
- 此阶段将删除文件名中的文件扩展名。参见第5行。
- 请记住第8行的lookup阶段。这个标签引用为lookup阶段创建了Secondary输入和输出流。网页列表进入lookup阶段的Secondary输入。lookup从Secondary流读取主记录。如果详细记录没有匹配的主记录,则将其写入lookup阶段的Secondary输出。Secondary输出流到下一个阶段。
- 此阶段查找以字符串Hostby开头的记录。如果找到,则将其写入Secondary输出流。
- 此阶段查找以字符串str开头的记录。比较时不区分大小写。如果找到记录,则将其写入Secondary输出。
- 此阶段查找以NFind开头的记录。如果找到,则将其写入Secondary输出。
- 此标签引用为第10行的faninany阶段创建了Secondary输入。这是第三个管道的最后一个阶段。
- 这是第四个管道的第一个阶段。这个标签引用定义了第27行nfind阶段的Secondary输出。
- 此阶段将链接标签设置为GetHost页面,该页面是已知的。
- 此标签引用为第10行的faninany阶段定义了Tertiary输入。
- 这是第五个管道的第一个阶段。这个标签引用str:定义了第28行nfind阶段的Secondary输出。
- if阶段在管道内创建一个If, Else, 和/或 Endif结构。在本例中,由标签if1:定义的if是If, Endif,由标签if2:定义的if是If, Else, Endif。if1:阶段将加载一个all阶段,该阶段查找不包含指示字符串的记录。如果记录满足all阶段指定的条件,则if阶段将其传递到其Primary输出。如果记录不满足all阶段设置的条件,则将其写入Secondary输出。Primary输出是“true”路径,Secondary输出是“false”路径(通过If, Else, Endif)。所有位于if阶段之后、直到流标签if1:下一次出现的所有阶段构成了true路径。所有位于第二个流标签之后、直到标签下一次出现的所有阶段构成了false路径。当true路径上的记录到达流标签时,它们被写入if阶段的Secondary输入。然后,if阶段将其Secondary输入写入其Tertiary输出流(如果已连接)。如果Tertiary输出未连接,则它们被写入Secondary输出。Tertiary输出实际上是“Endif”。当false路径上的记录到达流标签时,它们被写入if阶段的Tertiary输入。然后,if阶段将其Tertiary输入写入其Tertiary输出。
- 这是为if1:标签定义的if的true路径的第一个阶段。
- 这是为if2:标签定义的if的true路径的第一个阶段。
- 标签引用是内部if的Else。
- 这是为if2:标签定义的if的false路径的第一个阶段。
- 此标签引用是内部if的Endif。
- 此标签引用是外部ifif1:的Endif。
- 此标签引用为第10行的faninany阶段定义了Quartinary输入。
- 这是第六个管道的第一个阶段。这个标签引用nf:定义了第29行nfind阶段的Secondary输出。
- 创建NFind阶段的文档页面链接。
- 此标签引用为第10行的faninany阶段定义了Quinary输入。
- 这是第七个管道的第一个阶段。它接收来自第24行fanout阶段的数据。
- 对于每个输入文件名,此spec阶段会创建一个命令,将该文件复制到文档目录。
- 此shell阶段执行由前一个阶段创建的命令。
- console阶段将任何输入写入终端屏幕。这让用户知道哪些文件被复制了。
要查看此管道产生的输出,请在此处 转到。
此管道的运行时间为1.328125秒。
我将上面生成的表复制到一个网页中。下面的管道将该表转换为“Doc”目录的“index.html”网页。
- 'literal <HTML><Head>'+
- '|literal append \<Title>Stage Documentation</Title>\'+
- '|literal append \<META http-equiv="Content-Type" content="text/html; charset=UTF-8" />\'+
- '|literal append \<META http-equiv="Content-Style-Type" content="text/css" />\'+
- '|literal append \</Head><Body>\'+
- '|append < stagetable.html'+
- '|change \Doc/\\'+
- '|literal append \</Body></HTML>\'+
- '|> Doc\index.html'
- 第1至5行生成网页的标题。关于literal阶段的说明。如果未指定选项,它将写入其文字字符串,然后读取并写入其Primary输入。因此,如果没有append选项,标题行将与它们出现的顺序相反。append选项表示读取并写入任何输入数据,然后写入文字字符串。
- 参见第1行。
- 参见第1行。
- 参见第1行。
- 参见第1行。
- append阶段首先读取并写入任何输入数据。然后它加载指定的阶段并执行它。在本例中,它读取生成的表。
- 生成的表引用了“Doc”目录。但页面将位于“Doc”目录中。因此,我们需要从表中删除“Doc”引用。change阶段完成此操作。
- 这个literal阶段为网页添加了结束标签。
- 最后,我们写入“index.html”文件。
在PowerShell下运行
要从PowerShell脚本调用管道,你需要执行以下操作:
1. 将管道定义为字符串。例如:
$pipe = '(end ?) stem data'+ '|f:find abc'+ '|cons'+ '? f:'+ '|> notabc.txt'2. 要运行管道,请使用应用程序提供的Run-Pipe cmdlet。
run-pipe $pipe
有关在PowerShell中创建和使用管道的更多信息,请参阅HowTo。
摘要
本文档作为介绍,用于解释下面引用的IBM手册以及我将提供的任何文档中使用的语法。此外,我希望它能展示PipeLines的潜力。第二部分将讨论管道中的数据流。
参考文献
z/VM V5R3.0 Pipelines Reference
z/VM V5R2.0 CMS Pipelines User's Guide
CMS/TSO Pipelines Runtime Library Distribution
作者帮助文件