HP OpenVMS Systems Documentation

Content starts here

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


前页 后页 目录 索引