|
OpenVMS 用户手册
13.5.3 第 3 步: 添加循环
循环是重复执行直到满足条件的一组语句。循环操作如下所示:
- 从用户输入获得值
- 处理命令
- 重复处理,直到用户退出命令过程
要编写循环,遵循这个过程:
步骤 |
动作 |
1
|
从标号开始循环。
|
2
|
测试变量,确定是否需要执行循环中的命令。 |
3
|
如果不需要执行循环,则前去循环的末端。
|
4
|
如果需要执行循环,则执行在循环体中的命令,然后返回到循环的开始。
|
5
|
结束循环。
|
以下例子展示 CLEANUP.COM 命令过程中的循环使用:
$ GET_COM_LOOP:
$ INQUIRE COMMAND-
$ "Enter command (DELETE, DIRECTORY. EXIT, PRINT, PURGE, TYPE)"
$ IF COMMAND .EQS. "EXIT" THEN GOTO END_LOOP
$!
$! Execute if user entered DELETE
$ DELETE:
$ IF COMMAND .NES. "DELETE" THEN GOTO DIRECTORY
$ WRITE SYS$OUTPUT "This is the DELETE section."
$ GOTO GET_COM_LOOP
.
.
.
$ END_LOOP:
$ WRITE SYS$OUTPUT "Directory ''F$DIRECTORY()' has been cleaned"
$ EXIT
|
一旦命令执行,控制就传递回 GET_COM_LOOP
标号,直到用户输入 EXIT 命令。当输入 EXIT 命令时,这个过程输出一条消息声明目录已被清理。13.5.4 第 4 步: 结束命令过程
要结束命令过程,遵循这个过程:
步骤 |
动作 |
1
|
决定在哪里需要退出或放弃命令过程。
|
2
|
放置 EXIT 或 STOP 命令。
|
13.5.4.1 使用 EXIT 命令
可以把 EXIT 命令放在命令过程中:
- 确保过程不执行某些行
- 结束有一个以上执行路径的过程
- 结束命令过程
以下是一个使用 EXIT
命令的例子,可避免执行位于过程末端的出错处理例行程序:
.
.
.
$ EXIT ! End of normal execution path
$ ERROR_ROUTINE
.
.
.
|
以下是一个使用 EXIT 命令的例子,可结束有一个以上执行路径的过程:
$ START:
$ IF P1 .EQS. "TAPE" .OR. P1 .EQS. "DISK" THEN GOTO 'P1'
$ INQUIRE P1 "Enter device (TAPE or DISK)"
$ GOTO START
$ TAPE: !Process tape files
.
.
.
$ EXIT
$ DISK: ! Process disk files
.
.
.
$ EXIT
|
在每个标号 (TAPE 和 DISK) 后面的命令为过程提供不同路径。在 DISK
标号之前的 EXIT 命令确保不执行在 DISK 标号之后的命令,除非明确到达这个标号的过程分支。
在过程的末端不需要 EXIT 命令,因为过程的文件结束标记(end-of-file)
引起一个潜在的 EXIT 命令。然而,Compaq 建议您使用 EXIT 命令。13.5.4.2 使用 STOP 命令
在命令过程中,可以使用 STOP 命令确保如果发生严重出错则终止过程。如果 STOP 命令出现在交互执行的命令过程中,那么控制将返回到 DCL
级。如果命令过程以批量模式执行,则批量作业终止。
这个命令行告诉过程如果发生严重出错就停止:
$ ON SEVERE_ERROR THEN STOP
|
13.5.5 第 5 步: 测试和调试程序逻辑
一旦使用程序桩编写代码,就应该测试命令过程的全部逻辑。应该测试所有可能的执行路径。
遵循这个过程测试和调试命令过程:
步骤 |
动作 |
1
|
在命令过程中输入每个有效命令以测试程序逻辑。
|
2
|
输入无效命令以继续测试程序逻辑。
|
3
|
使用 EXIT 命令退出命令过程以完成测试程序逻辑。
|
4
|
如果必要,就使用 SET VERIFY、SET PREFIX 或
SHOW SYMBOL 命令来调试程序。
|
以下例子展示如何通过输入和执行每个可能命令、无效命令然后退出测试命令过程:
$ @CLEANUP
Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE): DELETE
This is the DELETE section.
Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE): DIRECTORY
This is the DIRECTORY section.
.
.
.
Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE): PRINF
You have entered an invalid command.
Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE): EXIT
$
|
13.5.5.1 调试命令过程
可以使用以下命令帮助调试命令过程:
- SET VERIFY
显示每行然后执行之。校验出错时,可查看这个错误和发生这个错误所在的行。可以使用 SET VERIFY 命令的关键字指出只校验某些命令行或数据行。 SET
VERIFY 命令保持有效,直到您注销、输入 SET
NOVERIFY 命令或者使用 F$VERIFY 词法函数更改校验设置。(第 15 章包含更改校验设置的详情。)
- SET PREFIX
如果校验生效,也可以使用 DCL 命令 SET
PREFIX
对过程日志文件标记时间戳,方法是在每个命令行前加上它的执行时间。
- SHOW SYMBOL
SHOW SYMBOL 命令可以用来确定符号是如何在过程中定义的。
例子: 使用 SET VERIFY 命令进行调试
在以下例子中,标号 END_LOP
拼写错误。因为启用校验,故可以精确地查看在哪里出错:
$ SET VERIFY
$ @CLEAN
$ GET_COM_LOOP:
$ INQUIRE COMMAND -
"Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE)"
Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE): EXIT
$ IF COMMAND .EQS. "EXIT" THEN GOTO END_LOP
%DCL-W-USGOTO, target of GOTO not found -
check spelling and presence of label
|
要纠正这个错误,可把标号更改为 END_LOOP。
例子: 使用 SET PREFIX 命令进行调试
以下例子说明时间戳的使用:
$ SET VERIFY
$ @TEST
$ SET DEFAULT SYS$LOGIN
$ SHOW DEFAULT
USER$:[SMYTHE]
$ SET PREFIX "(!5%T) "
$ @TEST
(17:52) $ SET DEFAULT SYS$LOGIN
(17:52) $ SHOW DEFAULT
USER$:[SMYTHE]
|
例子: 使用 SHOW SYMBOL 命令进行调试
以下例子展示 SHOW SYMBOL 命令是如何用来确定符号 COMMAND 是怎样定义的:
$ SET VERIFY
$ @CLEAN
$ GET_COM_LOOP:
$ INQUIRE COMMAND -
"ENTER COMMAND (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE)"
ENTER COMMAND (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE): EXIT
$ SHOW SYMBOL COMMAND
COMMAND = "EXIT"
$ IF COMMAND .EQS. "exit" THEN GOTO END_LOOP
.
.
.
|
SHOW SYMBOL 命令展现符号 COMMAND 的值为
"EXIT"。因为 INQUIRE 命令自动把输入转换为大写字母而测试命令的 IF 语句使用小写字母字符串 "exit",因此 DCL
确定这些字符串是不同的。要纠正这个错误,确定在 IF 语句中的引用字符串是以大写字母形式写入。这个字符串的其余部分可以使用大写字母,也可以使用小写字母。13.5.5.2 允许校验执行
当命令过程执行允许校验时,也可以中断它。当命令过程不包含 SET VERIFY 命令或 Ctrl/Y 组合键时,通过以下这些步骤可以允许校验:
步骤 |
动作 |
1
|
按下 Ctrl/Y 可中断执行。
|
2
|
输入 SET VERIFY 命令。
|
3
|
输入 CONTINUE 命令可继续执行命令过程 (允许校验)。
|
13.5.6 第 6 步: 添加清除任务
一般而言,命令过程的执行不要更改用户的进程状态。因此,命令过程应该包括一组把进程返回到它的原始状态的命令。这组命令通常是标号为 "CLEAN_UP"
的子例行程序部分。普通的清除操作包括关闭文件和重新设置默认设备和目录。
遵循这个过程可把清除任务添加到命令过程:
步骤 |
任务 |
1
|
从一个标号开始清除子例行程序,例如 CLEAN_UP。 |
2
|
使用 F$GETJPI 词法函数测试任何打开的文件。 |
3
|
使用 DELETE 或 PURGE 命令删除任何临时或无关的文件。 |
4
|
如果更改了任何默认 (例如设备或目录),则使用 SET DEFAULT 命令把它们恢复到它们的原始状态。
|
5
|
包括 ON CONTROL_Y 语句确保清除操作能执行。
|
13.5.6.1 关闭文件
如果打开了任何文件,在退出过程之前确保关闭它们。可以使用词法函数 F$GETJPI 检查保持进程的打开文件定额 (FILCNT)。如果 FILCNT
在命令过程的开始和结束是一样的,那么就知道已关闭所有文件。 这些命令是用于警告用户还有打开的文件:
$ FIL_COUNT = F$GETJPI ("","FILCNT")
.
.
.
$ IF FILCNT .NE. F$GETJPI ("", "FILCNT") THEN-
WRITE SYS$OUTPUT "WARNING -- file left open)
|
13.5.6.2 删除临时或无关文件
如果建立了临时文件,就删除它们。一般而言,如果更新了任何文件,就应该清除它们以删除以前的副本。在删除您尚未建立的文件之前,确定您要删除它们。例如,如果您更新了一个包含重要数据的文件,就可以可选地进行清除操作。
如果您更改了默认设备、目录,那么在退出命令过程之前重新设置原始默认。要保存原始的默认目录名,使用 F$ENVIRONMENT 词法函数的 DEFAULT 关键字。在命令过程的末端,包括一个 SET DEFAULT 命令恢复保存的设备和目录。
这个例子的命令行展示保存和恢复设备和目录的默认值:
$ SAV_DEFAULT = F$ENVIRONMENT ("DEFAULT")
.
.
.
$ SET DEFAULT 'SAV_DEFAULT'
|
13.5.6.3 通常更改的进程特征
下表列出其他通常更改的进程特征,词法函数用来保存进程特征,而词法函数或命令用来恢复进程特征:
特征 |
用于保存的词法函数 |
用于恢复的词法函数 |
DCL 提示
|
F$ENVIRONMENT
|
SET PROMPT
|
默认保护
|
F$ENVIRONMENT
|
SET PROTECTION/DEFAULT
|
特权
|
F$SETPRV
|
F$SETPRV 或 SET PROCESS/PRIVILEGES
|
控制字符
|
F$ENVIRONMENT
|
SET CONTROL
|
校验 |
F$VERIFY
|
F$VERIFY
|
消息格式
|
F$ENVIRONMENT
|
SET MESSAGE
|
键状态
|
F$ENVIRONMENT
|
SET KEY
|
有关这些词法函数的完整描述,请参阅
OpenVMS DCL Dictionary。13.5.6.4 确保执行清除操作
要确保即使命令过程异常结束也要执行清除操作,在命令过程中使用以下语句开始每个命令级:
$ ON CONTROL_Y THEN GOTO CLEANUP
|
有关使用 ON CONTROL_Y 命令的详情,请参阅第 14 章。13.5.7 第 7 步: 完成命令过程
当正确完成一般设计时,做以下几步完成命令过程:
步骤 |
任务 |
1
|
在命令过程中替换第一个程序桩的命令。 |
2
|
测试命令过程,确保新命令恰当地操作。 |
3
|
如果有必要调试命令过程。
|
4
|
第一个程序桩完成时,转向下一个,以此类推,直到替换所有程序桩。
|
例子: 用命令替换程序桩
以下例子展示 CLEANUP.COM 的 TYPE 段的代码:
$! Execute if user entered TYPE
$! TYPE:
$ IF COMMAND .NES. "TYPE THEN GOTO ERROR
$ INQUIRE FILE "File to type"
$ TYPE 'FILE'
$ GOTO GET_COM_LOOP
|
这将替换现有代码:
$ WRITE SYS$OUTPUT "This is the TYPE section."
|
例子: CLEANUP.COM 命令过程
以下是已完成的 CLEANUP.COM 命令过程的例子:
$ GET_COM_LOOP:
$ INQUIRE COMMAND -
"Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE)"
$ IF COMMAND .EQS. "EXIT" THEN GOTO END_LOOP
$!
$!Execute if user entered DELETE
$ DELETE:
$ IF COMMAND .NES. "DELETE" THEN GOTO DIRECTORY
$ INQUIRE FILE "File to delete? "
$ DELETE 'FILE'
$ GOTO GET_COM_LOOP
$!
$!Execute if user entered DIRECTORY
$ DIRECTORY:
$ IF COMMAND .NES. "DIRECTORY" THEN GOTO PRINT
$ DIRECTORY
$ GOTO GET_COM_LOOP
$!
$!Execute if user entered PRINT
$ PRINT:
$ IF COMMAND .NES. "PRINT" THEN GOTO PURGE
$ INQUIRE FILE "File to print? "
$ PRINT SYS$OUTPUT 'FILE'
$ GOTO GET_COM_LOOP
$!
$!Execute if user entered PURGE
$ PURGE:
$ IF COMMAND .NES. "PURGE" THEN GOTO TYPE
$ PURGE
$ GOTO GET_COM_LOOP
$!
$!Execute if user entered TYPE
$ TYPE:
$ IF COMMAND .NES. "TYPE" THEN GOTO ERROR
$ INQUIRE FILE "File to type"
$ TYPE 'FILE'
$ GOTO GET_COM_LOOP
$!
$ ERROR:
$ WRITE SYS$OUTPUT "You entered an invalid command."
$ GOTO GET_COM_LOOP
$!
$ END_LOOP:
$ WRITE SYS$OUTPUT "Directory ''F$DIRECTORY()' has been cleaned."
$
$ EXIT
|
13.6 执行命令过程
要使命令过程运行,必须执行它。您可以执行命令过程:
- 从另一个命令过程中
- 在远程节点上
- 作为参数或限定词给 DCL 命令
- 交互地
- 作为批量作业
- 在磁盘和磁带卷上
以下几节描述每一种方法。
13.6.1 在其他命令过程中执行命令过程
通过包括执行过程 (@) 命令,可以从一个命令过程内执行另一个命令过程。
以下命令过程 WRITEDATE.COM 调用命令过程
GETDATE.COM:
$! WRITEDATE.COM
$!
$ INQUIRE TIME "What is the current time in hh:mm format?"
$ @GETDATE [JONES.COM]GETDATE.COM
|
13.6.2 执行远程节点上的命令过程
您可以使用 TYPE 命令执行在远程节点上另一个帐户顶级目录的命令过程。可以执行命令过程:
- 显示不提供簇范围服务的本地 OpenVMS 簇系统的服务状态
- 列出注册到远程节点的用户
输入 TYPE 命令和后面的存取控制字符串。使用以下格式:
$ TYPE nodename"username password"::"TASK=command_procedure"
|
变量 username 和 password 是远程节点帐户的用户名和密码。
这个命令过程显示注册到该命令过程所驻留的远程节点的用户:
$!SHOWUSERS.COM
$!
$ IF F$MODE() .EQS. "NETWORK" THEN DEFINE/USER SYS$OUTPUT SYS$NET
$ SHOW USERS
|
在以下例子中,SHOWUSERS.COM 位于节点 ORIOLE 上的 BIRD 帐户的顶级目录中,而密码是
BOULDER。SHOWUSERS.COM 在远程节点 ORIOLE 执行 DCL 命令 SHOW USERS。TYPE
命令显示来自本地节点上 (即是在您输入 type 命令的终端)SHOWUSERS.COM
的输出:
$ TYPE ORIOLE"BIRD BOULDER"::"TASK=SHOWUSERS"
在 11-DEC-1999 17:20:13.30 时的 OpenVMS 用户进程
用户总数 = 4,进程数 = 4
用户名 节点 交互 子进程 批量
FLICKER AUTOMA 2 1
ROBIN FABLES 1 2 1
DOVE MURMUR 1
DUCK FABLES 1 1
|
13.6.2.1 安全注意事项
使用带存取控制字符串的 TYPE 命令时,您的密码将显现在终端上。要采取适当的安全预防措施,如第 18 章所述。13.6.3 执行带 DCL 限定词或参数的命令过程
您可以建立一个指定 DCL 命令参数或限定词的命令过程。当有一组参数或限定词频繁使用于一个或多个命令时,这类命令过程是有用的。
在将要经常指定限定词或参数的命令行中输入执行过程命令 (@)。
这个命令过程可以用来为 LINK 命令输入一组限定词:
$! This command procedure contains command
$! qualifiers for the LINK command.
$!
/DEBUG/SYMBOL_TABLE/MAP/FULL/CROSS_REFERENCE
|
使用 DEFLINK.COM 指定的限定词,这个命令行链接一个对象 SYNAPSE.OBJ:
这个命令过程可以用来为 DCL 命令输入参数 CHAP1.TXT、CHAP2.TXT 和 CHAP3.TXT:
$! PARAM.COM
$! This command procedure contains a list of
$! parameters that can be used with commands.
$!
CHAP1, CHAP2, CHAP3
|
这个命令行指定命令过程 PARAM 代替一列参数。在以下例子中,参数是在 PARAM.COM 中列出的文件名:
注意
使用执行过程命令 (@) 时,DCL 把整个指定文件视为命令输入。 |
|