OpenVMS 用户手册
14.12.2 使用 /INDEX 和 /KEY 限定词
要随机地从索引顺序文件读取记录,使用 READ
命令限定词 /INDEX 和 /KEY。这些限定词指定应该这样从文件读取记录:在索引中查找指定的键并返回与这个键关联的记录。如果不指定一个索引,则使用主索引 (0)。
在随机地读取一个记录之后,使用不带 /KEY 或 /INDEX 限定词的 READ 命令可以顺序地读取文件的剩余部分。
14.12.3 使用 /DELETE 限定词
您可以使用带 /DELETE 限定词的 READ 命令从索引顺序文件中删除记录。/DELETE 限定词使一个记录在读取之后从文件删除。使用带 /INDEX 和
/KEY 限定词的
/DELETE 限定词可删除一个给定键指定的记录。
有关 /DELETE、/INDEX 和 /KEY 限定词的详情,请参阅 OpenVMS DCL Dictionary 对 READ 命令的描述。14.13 使用关闭命令
CLOSE 命令关闭文件,并解除由 OPEN
命令建立的逻辑名的赋值。确信在命令过程终止之前,关闭在命令过程中打开的所有文件。如果不能关闭打开的文件,那么当命令过程终止时这个文件仍然保持打开,并且已分配给打开文件的逻辑名不会从进程逻辑名表删除。
在以下例子中,CLOSE 命令关闭文件 STATS.DAT 并解除逻辑名 INFILE
赋值:
$ OPEN INFILE DISK4:[MURPHY]STATS.DAT
.
.
.
$ CLOSE INFILE
|
14.14 修改文件
本节描述修改文件的三种方法:
14.14.1 更新记录
使用更新方法修改记录时,您可以对文件的少量记录进行较小更改。因为这个方法不允许您更改记录的大小或者文件中的记录数,因此只对格式记录的文件才使用它 (例如,在数据文件中)。
要对文件进行较小更改,使用这个过程:
步骤 |
动作 |
1
|
打开有读和写存取权文件。 |
2
|
使用 READ 命令通读文件,直至想要修改的记录。
|
3
|
修改这个记录。 在顺序文件中,这个记录的文本大小必须完全与原始记录一样。如果修改记录的文本较短,那么用空格记录填充,把空格添加到修改记录的末端,直到它与原始记录长度相同。如果修改记录的文本较长,则需要建立新文件。
|
4
|
使用 WRITE/UPDATE 命令把修改记录写回原来的文件。 |
5
|
重复步骤 2 至 4,直到更改了打算更改的所有记录。 |
6
|
使用 CLOSE 命令关闭这个文件。 您关闭这个文件之后,它包含与开始时同样的版本号,即使个别记录已被更改。
|
以下命令过程展示如何通过读取和更新个别记录更改顺序文件:
$! Open STATS.DAT and assign it the logical name FILE
$!
$ OPEN/READ/WRITE FILE DISK4:[MURPHY]STATS.DAT
$ BEGIN_LOOP:
$! Read the next record from FILE into the symbol RECORD
$ READ/END_OF_FILE=END_LOOP FILE RECORD
$! Display the record and see if the user wants to change it
$! If yes, get the new record. If no, repeat loop
$!
$ PROMPT:
$ WRITE SYS$OUTPUT RECORD
$ INQUIRE/NOPUNCTUATION OK "Change? Y or N [Y] "
$ IF OK .EQS. "N" THEN GOTO BEGIN_LOOP
$ INQUIRE NEW_RECORD "New record"
$! Compare the old and new records
$! If old record is shorter than new record, issue an
$! error message. If old record and new record are the
$! same length, write the record. Otherwise pad the new
$! record with spaces so it is correct length
$!
$ OLD_LEN = F$LENGTH(RECORD)
$ NEW_LEN = F$LENGTH(NEW_RECORD)
$ IF OLD_LEN .LT. NEW_LEN THEN GOTO ERROR
$ IF OLD_LEN .EQ. NEW_LEN THEN GOTO WRITE_RECORD
$ SPACES = " "
$ PAD = F$EXTRACT(0,OLD_LEN-NEW_LEN,SPACES)
$ NEW_RECORD = NEW_RECORD + PAD
$!
$ WRITE_RECORD:
$ WRITE/UPDATE FILE NEW_RECORD
$ GOTO BEGIN_LOOP
$!
$ ERROR:
$ WRITE SYS$OUTPUT "Error -- New record is too long"
$ GOTO PROMPT
$!
$ END_LOOP:
$ CLOSE FILE
$ EXIT
|
系统把记录显示在终端上,并问您记录是否需要修改。如果您选择修改记录,就从终端读取新记录,并且把它的长度与原始记录长度进行比较。如果原始记录较长,那么填充额外的空格使新记录有同样的大小。如果原始记录较短,则系统显示出错消息并且再次提示输入新记录。14.14.2 建立新的输出文件
要对文件进行大的更改,打开用于读存取的文件并且打开用于写存取的新文件。因为是建立新的输出文件,因此可以修改记录的大小、添加记录、删除记录或插入记录。
OPEN/WRITE 命令打开新文件用于写存取。新文件可以有与原始文件同样的名称,并且版本号比旧文件的版本号大 1。
注意
要确保打开正确的文件用于读取,在打开用于写存取的新版本之前,必须打开用于读存取的现有文件。 |
要建立可以修改的文件,使用以下过程:
步骤 |
动作 |
1
|
打开用于读存取的文件。
这是输入文件,也是您修改的文件。
|
2
|
打开一个用于写存取的新文件。
这是输出文件,也是您建立的文件。如果给出的输出文件与输入文件同名,那么输出文件的版本号将有比输入文件大 1
。
|
3
|
使用 READ 命令从修改的文件读取每个记录。
当您从原始文件读取每个记录时,决定如何处理这个记录。
|
4
|
继续读取和处理记录,直到完成。
|
5
|
使用 CLOSE 命令关闭输入文件和输出文件。
|
在以下表格中,符号 RECORD 包含从原始文件读取的记录:
如果记录是 |
那么 |
不更改
|
把同样的符号写入新文件。
|
更改
|
使用 INQUIRE 命令把不同的记录读入这个符号,然后把这个修改符号写入新文件。
|
删除
|
不把这个符号写入新文件。
|
插入
|
使用一个循环把记录读入这个符号,然后把这个符号写入新文件。
|
例子: 修改记录
- 在以下例子中,符号 NEW_FILE 被写入新文件:
$ ! No change
$ WRITE NEW_FILE RECORD
|
- 在以下例子中,INQUIRE 命令用来把一个已修改的符号写入新文件:
$ ! Change
$ INQUIRE NEW_RECORD "New record"
$ WRITE NEW_FILE NEW_RECORD
|
- 在以下例子中,使用一个循环把符号写入新文件:
$ ! Insertion
$LOOP:
$ !Get new records to insert
$ INQUIRE NEW_RECORD "New record"
$ IF RECORD .EQS. "" THEN GOTO END_LOOP
$ WRITE NEW_FILE NEW_RECORD
$ GOTO LOOP
$END_LOOP:
|
例子: 建立输出文件
以下例子展示一个命令过程,它从输入文件读取记录,处理这个记录,然后把这个记录复制到一个输出文件:
$! Open STATS.DAT for reading and assign it
$! the logical name INFILE
$! Open a new version of STATS.DAT for writing
$! and assign it the logical name OUTFILE
$!
$ OPEN/READ INFILE DISK4:[MURPHY]STATS.DAT
$ OPEN/WRITE OUTFILE DISK4:[MURPHY]STATS.DAT
$!
$ BEGIN_LOOP:
$! Read the next record from INFILE into the symbol RECORD
$!
$ READ/END_OF_FILE=END_LOOP INFILE RECORD
$! Display the record and see if the user wants to change it
$! If yes, get the new record
$! If no, write record directly to OUTFILE
$!
$ PROMPT:
$ WRITE SYS$OUTPUT RECORD
$ INQUIRE/NOPUNCTUATION OK "Change? Y or N [Y] "
$ IF OK .EQS. "N" THEN GOTO WRITE_RECORD
$ INQUIRE RECORD "New record"
$!
$ WRITE_RECORD:
$ WRITE OUTFILE RECORD
$ GOTO BEGIN_LOOP
$!
$! Close input and output files
$ END_LOOP:
$ CLOSE INFILE
$ CLOSE OUTFILE
$ EXIT
|
14.14.3 为文件附加记录
使用以下过程 (OPEN/APPEND 命令) 把记录附加到现有文件的末端:
步骤 |
动作 |
1
|
使用带 /APPEND 限定词的 OPEN 命令把记录指针定位在文件尾。
/APPEND 限定词不建立文件的新版本。
|
2
|
使用 WRITE 命令写入新的数据记录。 |
3
|
继续添加记录,直到完成。
|
4
|
使用 CLOSE 命令关闭这个文件。
|
以下命令过程把记录附加到文件 STATS.DAT 的末端:
$! Open STATS.DAT to append files and assign
$! it the logical name FILE
$!
$ OPEN/APPEND FILE DISK4:[MURPHY]STATS.DAT
$!
$ BEGIN_LOOP:
$! Obtain record to be appended and place this
$! record in the symbol RECORD
$!
$ PROMPT:
$ INQUIRE RECORD -
"Enter new record (press RET to quit) "
$ IF RECORD .EQS. "" THEN GOTO END_LOOP
$! Write record to FILE
$!
$ WRITE FILE RECORD
$ GOTO BEGIN_LOOP
$!
$! Close FILE and exit
$!
$ END_LOOP:
$ CLOSE FILE
$ EXIT
|
14.15 处理文件 I/O 出错
对 OPEN、READ 或 WRITE 命令使用 /ERROR
限定词可抑制系统出错消息,并把控制传递到指定的标号。如果在输入或输出操作期间出错,那么
/ERROR 限定词取代所有其他出错控制机制 (除了在 READ 命令上的 /END_OF_FILE 限定词之外)。
以下例子对 OPEN 命令使用 /ERROR 限定词:
$ OPEN/READ/ERROR=CHECK FILE CONTINGEN.DOC
.
.
.
$ CHECK:
$ WRITE SYS$OUTPUT "Error opening file"
|
OPEN 命令请求打开文件 CONTINGEN.DOC 用于读取。如果不能打开这个文件 (例如,这个文件不存在),那么 OPEN 命令返回一个出错条件,并把控制传递到 CHECK 标号。
/ERROR 限定词指定的出错路径取代其命令级建立的当前 ON
条件。如果出错并且目标标号被成功地给出控制,那么保留的全局符号
$STATUS 保持出错代码。可以在出错处理例行程序中使用
F$MESSAGE 词法函数来显示 $STATUS 中的消息。
在以下例子中,词法函数 F$MESSAGE 用来显示 F$STATUS 词汇的内容:
$ OPEN/READ/ERROR=CHECK FILE 'P1'
.
.
.
$ CHECK:
$ ERR_MESSAGE = F$MESSAGE($STATUS)
$ WRITE SYS$OUTPUT "Error opening file: ",P1
$ WRITE SYS$OUTPUT ERR_MESSAGE
.
.
.
|
14.15.1 默认出错动作
使用 OPEN、READ、WRITE 或 CLOSE
命令时,如果出错,并且没有指定出错动作,那么采取当前的 ON 命令动作。
READ 命令接收一条文件结束消息时,其出错动作确定如下:
- 如果使用 /END_OF_FILE
限定词,控制被传递到指定的标号。
- 如果没有使用 /END_OF_FILE
限定词,控制就被传递到
/ERROR 限定词指定的标号。
- 如果没有指定任意一个限定词 (/END_OF_FILE 或 /ERROR),那么采取当前的 ON 命令动作。
14.16 控制执行流的技术
命令过程的正常执行流是顺序的:
过程中的命令依次执行,直到文件尾。然而,您也可以控制某些语句是否执行,或者过程在什么条件下应该继续执行。
以下几节论述:
- 可以用来控制或改变执行流的 DCL 命令:
- IF,THEN,ELSE
- GOTO
- GOSUB
- CALL
- 使用命令块
- 编写 Case 语句
- 编写循环
14.16.1 使用 IF 命令
IF 命令测试表达式的值,当表达式的结果为真时执行一个命令或一块命令。当表达式的结果为假时,发生以下情况之一:
- 当一个命令跟随 THEN
命令时,就不执行它,而执行后面的命令。
- 当一块命令跟随 THEN 命令而没有指定 ELSE
命令时,这个命令就立即执行后面的 ENDIF
命令。
- 指定 ELSE 命令时,就执行这个命令或跟随 ELSE 命令的一块命令。
DCL 为 IF 命令提供两个不同格式。当为这个 IF 命令指定的表达式为真时,第一个格式执行单个命令,如第 13 章所述。
DCL 也提供一个块结构 IF 格式。如果指定的表达式为真,那么块结构 IF
命令执行一个以上命令,并且接受可选的ELSE
语句,如果表达式为假它执行一个或多个命令。
14.16.2 使用 THEN 命令
当表达式为真时要执行一个以上命令,可指定 THEN 命令为一个动词 (前面加上美元符的 DCL 命令),并且用 ENDIF 语句终止作为结果的块结构语句。
在以下例子中,THEN 语句用作一个动词:
$ IF expression
$ THEN
$ command
$ command
.
.
.
$ ENDIF
|
14.16.3 使用 ELSE 命令
当表达式为假时要执行一个或多个命令,可指定 ELSE 语句为一个动词,并且用 ENDIF 语句终止作为结果的块结构语句。
在以下例子中,ELSE 命令用作一个动词:
$ IF expression
$ THEN
$ command
$ command
.
.
.
$ ELSE
$ command
$ command
.
.
.
$ ENDIF
|
14.16.4 使用命令块
命令块可以按几种方法执行,这取决于您是把这些命令放在同一命令过程,或是把它们放入另一个命令过程并在那里执行。其指引如下:
- 如果把这些命令放在同一命令过程中,那么把它们放在 THEN 语句之后。例如:
$ IF condition
$ THEN command
command
.
.
.
$ ENDIF
|
- 如果把这些命令放入一个单独的过程,那么调用这个命令过程以作为 THEN 语句的一部分。例如:
$ IF condition
$ THEN @command_procedure
$ ELSE command
$ command
$ ENDIF
|
- 可以继续指定非块结构的 IF 格式,并且当满足指定的条件时把控制流指引到标号的区域。例如:
$ IF not condition THEN GOTO END_LABEL
.
.
.
$END_LABEL:
|
当 IF 表达式的结果为真时,您可以执行在 THEN
命令之后的一块命令。使用一块命令时,把 THEN 命令作为第一个命令放在跟随 IF 命令的命令行上。
在以下例子中,当 F$MODE 等于
"INTERACTIVE" 时,执行两个 SET TERMINAL 命令并且把这个过程的控制传递给标号 PROCEED。当 F$MODE 不等于
"INTERACTIVE" 时,退出这个过程。
$ IF F$MODE () .EQS. "INTERACTIVE"
$ THEN
$ SET TERMINAL/DEVICE=VT320
$ SET TERMINAL/WIDTH=132
$ GOTO PROCEED
$ ENDIF
$ EXIT
$PROCEED:
|
以下例子说明如何使用 IF 命令和 ELSE 命令连在一起的命令块:
$ INQUIRE DEV "Device to check"
$ IF F$GETDVI(DEV, "EXISTS")
$ THEN
$ WRITE SYS$OUTPUT "The device exists."
$ SHOW DEVICE 'DEV'
$ SET DEVICE/ERROR_LOGGING 'DEV'
$ ELSE
$ WRITE SYS$OUTPUT "The device does not exist."
$ WRITE SYS$OUTPUT "Error logging has not been enabled."
$ ENDIF
$ EXIT
|
当条件为真时,这个过程把一条消息写入
SYS$OUTPUT 并执行 SHOW DEVICE 和 SET DEVICE 命令。当条件不为真时,这个过程把两条消息写入
SYS$OUTPUT。
您使用 IF-THEN-ELSE 语言结构时,遵循以下限制:
- IF 语句的嵌套级不能超过 15 级。
- 使用 ELSE 或 ENDIF 语句终止 THEN 语句开始的命令块。
- 使用 ENDIF 语句终止 ELSE 语句开始的命令块。
- 在
IF 语句之后使用 THEN 语句作为第一个可执行语句。
- 不要在包含 THEN 或 ELSE
语句的命令行上指定标号。然而,您可以在包含 ENDIF 语句的命令行上指定标号。程序可以在当前命令块内分支,但是不建议分支到另一个命令块的中间。
真表达式
这个跟随 IF
命令的表达式可以包括一个或多个数字常数、串文字、符号名或词法函数,并且用逻辑、算术或字符串运算符分隔。如果一个表达式有以下值之一,它就为真:
- 奇数值
- 从字母 Y、y、T 或 t 开始的字符串值
- 包含可形成奇数值的数字的字符串值 (例如,字符串 "27")
假表达式
如果一个表达式有以下值之一,它就为假:
- 偶数值
- 不是从字母 Y、y、T 或 t 开始的字符串值
- 包含可形成偶数值的数字的字符串值 (例如,字符串 "28")
编写表达式
为 IF 命令编写表达式时,坚持以下规则:
- 在 IF 语句中使用符号时,它们的值被自动替换。不必使用省略号 (') 作为替换运算符,除非需要强制迭代翻译。
- 字符串比较运算符结束于字母 S。例如,运算符 .EQS.、.LTS. 和
.GTS. 用于字符串比较。相反,运算符 .EQ.、.LT. 和 .GT. 用于整数比较。
- 当测试查看两个字符串是否相同时,DCL
查找的匹配字符串必须使用同样的字母大小写和次序。即是说,字符串 "COPY" 不等于字符串 "copy" 或字符串 "CoPy"。
以下例子说明可以与 IF 命令一起使用的表达式。对于其他例子,请参阅 OpenVMS DCL Dictionary对 IF 命令的描述。
第一个例子使用一个逻辑运算符,并且只执行跟随 THEN 语句的一个命令。当符号 CONT 不为真时,退出这个过程。
$ INQUIRE CONT "Do you want to continue [Y/N]"
$ IF .NOT. CONT THEN EXIT
.
.
.
|
以下例子在 IF 表达式中使用一个符号和一个标号:
$ INQUIRE CHANGE "Do you want to change the record [Y/N]"
$ IF CHANGE THEN GOTO GET_CHANGE
.
.
.
$ GET_CHANGE:
.
.
.
|
当符号 CHANGE 为真时,过程把控制传递到标号 GET_CHANGE。否则,过程执行跟随 IF 命令的命令。
下一个例子说明两个不同的 IF 命令:
$ COUNT = 0
$ LOOP:
$ COUNT = COUNT + 1
$ IF COUNT .EQ. 9 THEN EXIT
$ IF P'COUNT' .EQS. "" THEN EXIT
.
.
.
$ GOTO LOOP
|
第一个 IF 命令比较两个整数;第二个 IF 命令比较两个字符串。注意,.EQ. 运算符用于整数比较,而 .EQS. 运算符用于字符串比较。
首先,COUNT 值与整数 9
进行比较。当这两个值相同时,就退出这个过程;当这两个值不相同时,过程继续。处理八个参数
(允许的最大数) 后循环终止。
在第二个 IF 表达式中,符号 P'COUNT' 的字符串值与空串进行比较查看这个符号是否是未定义的。注意,必须使用省略号强制对符号 COUNT 进行迭代替换。例如,当 COUNT 是 2
时,第一个翻译的结果是 P2。然后,P2 的值使用在字符串比较中。
当 IF
表达式的结果为真时,您也可以执行一个单独的命令过程。以下例子当 IF 表达式的结果为真时,执行命令过程 EXIT_ROUTINE.COM:
$ GET_COMMAND_LOOP:
$ INQUIRE COMMAND -
"Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE)"
$ IF COMMAND .EQS. "EXIT" THEN @EXIT_ROUTINE
|
|