|Document revision date: 15 July 2002|
The GOTO command passes control to a labeled line in a command procedure. (For additional information on label usage, refer to Chapter 13.) The GOTO command is especially useful within a THEN clause to cause a procedure to branch forward or backward. For example, when you use parameters in a command procedure, you can test the parameters at the beginning of the procedure and branch to the appropriate label.
The target label of a GOTO or GOSUB command cannot be inside either a separate IF-THEN-ELSE construct or a separate subroutine.
In the following example, the IF command checks that P1 is not a null string:
$ IF P1 .NES. "" THEN GOTO OKAY $ INQUIRE P1 "Enter file spec" $ OKAY: $ PRINT/COPIES=10 'P1' . . .
If P1 is a null string, the GOTO command is not executed and the INQUIRE command prompts for a parameter value. Otherwise, the GOTO command causes a branch around the INQUIRE command. In either case, the procedure executes the PRINT command following the line labeled OKAY.
In the following example the GOTO command returns an error message because its target (TEST_1) is within an IF-THEN construct:
$ GOTO TEST_1 $ EXIT $ IF 1.EQ.1 $ THEN WRITE SYS$OUTPUT "What are we doing here?" $ TEST_1: $ WRITE SYS$OUTPUT "Got to the label" $ ENDIF $ EXIT
You can also use the GOTO command to avoid reexecuting parts of the job that have completed successfully. To do this, follow these steps:
|1||Begin each possible starting point in the procedure with a label.|
After the label, use the SET RESTART_VALUE = label-name command to set
the restarting point to that label.
If the batch job is interrupted after the SET RESTART_VALUE = label-name command executes, the system assigns the appropriate label name to the global symbol BATCH$RESTART when the batch job restarts.
At the beginning of the procedure, test the value of the symbol
If $RESTART is true, execute a GOTO statement using the symbol BATCH$RESTART as the transfer label.
$RESTART Global Symbol
$RESTART is a reserved global symbol that the system maintains for you. $RESTART is true if one of your batch jobs was restarted after it was interrupted. Otherwise, $RESTART is false. You cannot delete the reserved global symbol $RESTART.
If a command procedure has SET RESTART_VALUE commands in it but you want the job to reexecute in its entirety, enter the SET ENTRY/NOCHECKPOINT command to delete the global symbol BATCH$RESTART. If you restart a job that was interrupted, the job starts executing in the section where it was interrupted.
This command procedure shows how to use restart values in a batch job:
$ ! Set default to the directory containing $ ! the file to be updated and sorted $ SET DEFAULT DISK1:[ACCOUNTS.DATA84] $ $ ! Check for restarting $ IF $RESTART THEN GOTO 'BATCH$RESTART' $ $ UPDATE_FILE: $ SET RESTART_VALUE = UPDATE_FILE . . . $ SORT_FILE: $ SET RESTART_VALUE = SORT_FILE . . . EXIT
To submit this command procedure as a batch job that can be restarted, use the /RESTART qualifier to the SUBMIT command when you submit the job. Because interrupted jobs begin executing in the section where they are interrupted, if this job is interrupted during the SORT_FILE routine, it starts executing at the label SORT_FILE when it is restarted.
Most of your process environment is not maintained when the system
fails. The only symbols maintained across a system failure are $RESTART
and BATCH$RESTART. Therefore, you should redefine any symbols or
process logical names used in your command procedure after each SET
RESTART_VALUE command or in a THEN block that executes if $RESTART is
If you define symbols and logical names in a THEN block, the command GOTO 'BATCH$RESTART' should be the last command in the THEN block.
14.16.6 Using the GOSUB and RETURN Commands
The GOSUB command transfers control to a labeled subroutine in a command procedure. If the label does not exist in the command procedure, the procedure cannot continue executing and is forced to exit. (For complete information on labels, refer to Chapter 13.) You can nest the GOSUB command up to 16 times per procedure level.
The GOSUB command is a local subroutine call; it does not create a new procedure level. Consequently, all labels and local symbols defined in the current command level are available to a subroutine invoked with GOSUB.
The RETURN command terminates a subroutine and returns control to the command following the GOSUB command. You can specify a value for $STATUS with the RETURN command that overrides the value that DCL assigns to $STATUS at the end of the subroutine. This value must be an integer between zero and four or an equivalent expression. If you specify a value for $STATUS, DCL interprets this value as a condition code. If you do not specify a value for $STATUS, the current value of $STATUS is saved.
The following example shows how you can use the GOSUB command to transfer control to subroutines:
$! $! GOSUB.COM $! $ SHOW TIME $ GOSUB TEST1 (1) $ WRITE SYS$OUTPUT "GOSUB level 1 has completed successfully." $ SHOW TIME $ EXIT $! $! TEST1 GOSUB definition $! $ TEST1: $ WRITE SYS$OUTPUT "This is GOSUB level 1." $ GOSUB TEST2 (2) $ RETURN %X1 (3) $! $! TEST2 GOSUB definition $! $ TEST2: $ WRITE SYS$OUTPUT "This is GOSUB level 2." $ WAIT 00:00:02 $ RETURN (4)
As you examine the example, note the following:
There are two ways to create new command levels:
The CALL command transfers control to a labeled subroutine in a command procedure and creates a new procedure level. The CALL command allows you to keep more than one related command procedure in a single file, making the procedures easier to manage. The subroutine label, which must be unique, can precede or follow the CALL command in the command procedure. Chapter 13 contains rules for entering subroutine labels.
In addition to the label, you can pass up to eight optional parameters to the subroutine. For complete information on parameters, refer to Section 14.2.
Following are rules for using the CALL command:
Following are additional defaults associated with using the CALL command:
The SUBROUTINE and ENDSUBROUTINE commands define the beginning and the end of a CALL subroutine. The label defining the entry point to the subroutine immediately precedes the SUBROUTINE command. You can place the EXIT command immediately before the ENDSUBROUTINE command but it is not required to terminate the subroutine. The ENDSUBROUTINE command terminates the subroutine and transfers control to the command line immediately following the CALL command.
Command lines in a subroutine execute only when the subroutine is called with the CALL command. During the line-by-line execution of the command procedure, the command language interpreter skips all commands between the SUBROUTINE and the ENDSUBROUTINE commands.
The following restrictions apply to defining the scope of subroutine entry points and the use of label references:
In the following example, the call is not valid because the CALL BAR command is located outside of the MAIN subroutine:
$ CALL BAR $ $ MAIN: SUBROUTINE $ $ BAR: SUBROUTINE $ ENDSUBROUTINE $ $ ENDSUBROUTINE
For this CALL command to work, it must be placed within the SUBROUTINE and ENDSUBROUTINE points.
The call shown in this example in not allowed because it is within an IF-THEN-ELSE block:
$ IF 1 $ THEN $ BOB:SUBROUTINE $ ENDSUBROUTINE $ ENDIF $ CALL BOB
The following example includes two subroutines called SUB1 and SUB2. The subroutines do not execute until they are called with the CALL command.
$ $! CALL.COM $ $! Define subroutine SUB1. $! $ SUB1: SUBROUTINE . . . $ CALL SUB2 !Invoke SUB2 from within SUB1. . . . $ @FILE !Invoke another command procedure file. . . . $ EXIT $ ENDSUBROUTINE !End of SUB1 definition. $! $! Define subroutine SUB2. $! $ SUB2: SUBROUTINE $ EXIT $ ENDSUBROUTINE !End of SUB2 definition. $! $! Start of main routine. At this point, both SUB1 and SUB2 $! have been defined but none of the previous commands have $! been executed. $! $ START: $ CALL/OUTPUT=NAMES.LOG SUB1 "THIS IS P1" . . . $ CALL SUB2 "THIS IS P1" "THIS IS P2" . . . $ EXIT !Exit this command procedure file.
The CALL command invokes the subroutine SUB1 and directs output to the
file NAMES.LOG. Subroutine SUB1 calls subroutine SUB2. The procedure
executes SUB2, invoking the command procedure FILE.COM with the execute
procedure (@) command. When all the commands in SUB1 have executed, the
CALL command in the main procedure calls SUB2 a second time. The
procedure exits when SUB2 finishes executing.
14.18 Writing Case Statements
A case statement is a special form of conditional code that executes one out of a set of command blocks, depending on the value of a variable or expression. Typically, the valid values for the case statement are labels at the beginning of each command block. The case statement passes control to the appropriate block of code by using the specified value as the target label in a GOTO statement.
To write a case statement, you must:
To list the labels, equate a symbol to a string that contains a list of the labels delimited by slashes (or any character you choose to act as a delimiter). This symbol definition should precede the command blocks.
The following example equates the symbol COMMAND_LIST to the labels PURGE, DELETE and EXIT:
$ COMMAND_LIST = "/PURGE/DELETE/EXIT/"
To write the case statement, follow this procedure:
|1||Use the INQUIRE command to get the value of the case variable.|
|2||Use the IF command with F$LOCATE and F$LENGTH to determine whether the value of the case variable is valid.|
If the case variable is valid, execute the case statement (with a GOTO
command) to pass control to the appropriate block of code.
Otherwise, display a message and exit or request a different case value.
In the following example, the label is equated to the full command name. Therefore, F$LOCATE includes the delimiters in its search for the command name to ensure that the command is not abbreviated.
$GET_COMMAND: $ INQUIRE COMMAND - "Command (EXIT,PURGE,DELETE)" $ IF F$LOCATE ("/"+COMMAND+"/",COMMAND_LIST) .EQ. - F$LENGTH (COMMAND_LIST) THEN GOTO ERROR_1 $ GOTO 'COMMAND' . . . $ERROR_1: $ WRITE SYS$OUTPUT "No such command as ''COMMAND'." $ GOTO GET_COMMAND
Each block of commands may contain one or more commands. Begin each command block with a unique label. End each command block by passing control to a label outside the list of command blocks.
In the following example, each block of commands begins with a unique label (PURGE:, DELETE:) and is ended by passing control to a label (GOTO GET_COMMAND) outside of the current command block:
$GET_COMMAND: . . . $PURGE: $ INQUIRE FILE $ PURGE 'FILE' $ GOTO GET_COMMAND $ ! $DELETE: $ INQUIRE FILE $ DELETE 'FILE' $ GOTO GET_COMMAND $ ! $EXIT:
You can write loops that test variables at the beginning of the command block (as described in Chapter 13). However, you can also write loops that test the termination variable at the end of the loop, by following this procedure:
|1||Begin the loop.|
|2||Perform the commands in the body of the loop.|
|3||Change the termination variable.|
Test the termination variable.
If the condition is not met, go to the beginning of the loop.
|5||End the loop.|
Note that when you test the termination variable at the end of the loop, the commands in the body of the loop are executed at least once, regardless of the value in the termination variable.
Both of the command blocks shown in this example execute a loop that terminates when COMMAND equals "EX" (EXIT). F$EXTRACT truncates COMMAND to its first two characters. In the first example, COMMAND, the termination variable, is tested at the beginning of the loop; in the second, it is tested at the end of the loop.
$ ! EXAMPLE 1 $ ! $GET_COMMAND: $ INQUIRE COMMAND- "Command (EXIT,DIRECTORY,TYPE,PURGE,DELETE,COPY)" $ COMMAND = F$EXTRACT(0,2,COMMAND) $ IF COMMAND .EQS. "EX" THEN GOTO END_LOOP . . . $ GOTO GET_COMMAND $END_LOOP:
$ ! EXAMPLE 2 $ ! $GET_COMMAND: $ INQUIRE COMMAND- "Command (EXIT,DIRECTORY,TYPE,PURGE,DELETE,COPY)" $ COMMAND = F$EXTRACT(0,2,COMMAND) . . . $ IF COMMAND .NES. "EX" THEN GOTO GET_COMMAND $ ! End of loop
To perform a loop a specific number of times, use a counter as the termination variable. In the following example, 10 file names are input by the user and placed into the local symbols FIL1, FIL2, ..., FIL10:
$ NUM = 1 ! Set counter $LOOP: ! Begin loop $ INQUIRE FIL'NUM' "File" ! Get file name $ NUM = NUM + 1 ! Update counter $ IF NUM .LT. 11 THEN GOTO LOOP ! Test for termination $END_LOOP: ! End loop . . .
The following example uses a counter to control the number of times a loop executes. The loop executes 10 times; the termination variable is tested at the end of the loop:
$! Obtain 10 file names and store them in the $! symbols FILE_1 to FILE_10 $! $ COUNT = 0 $ LOOP: $ COUNT = COUNT + 1 $ INQUIRE FILE_'COUNT' "File" $ IF COUNT .LT. 10 THEN GOTO LOOP $! $ PROCESS_FILES: . . .
The symbol COUNT is used to record the number of times the commands in the loop are executed. COUNT is also used to create the symbol names FILE_1, FILE_2, and so on to FILE_10. Note that the value of COUNT is incremented at the beginning of the loop but is tested at the end of the loop. Therefore, when COUNT is incremented from 9 to 10, the loop executes a last time (obtaining a value for FILE_10) before the IF statement finds a false result.
To perform a loop for a known sequence of values, use the F$ELEMENT lexical function. The F$ELEMENT lexical function obtains items from a list of items separated by delimiters. You must supply the item number, the item delimiter, and the list as arguments for F$ELEMENT.
For more information on how to use the F$ELEMENT lexical function, refer to the OpenVMS DCL Dictionary.
In the following example, the files CHAP1, CHAP2, CHAP3, CHAPA, CHAPB, and CHAPC are processed in order:
$ FILE_LIST = "1,2,3,A,B,C" $ INDEX = 0 $PROCESS: $ NUM = F$ELEMENT(INDEX,",",FILE_LIST) $ IF NUM .EQS. "," THEN GOTO END_LOOP $ FILE = "CHAP''NUM'" $ ! process file named by FILE . . . $ INDEX = INDEX + 1 $ GOTO PROCESS $END_LOOP: $ EXIT
In the following example, the command procedure uses a loop to copy the files listed in the symbol FILE_LIST to a directory on another node:
$ FILE_LIST = "CHAP1/CHAP2/CHAP3/CHAP4/CHAP5" $ NUM = 0 $! $! Process each file listed in FILE_LIST $ PROCESS_LOOP: $ FILE = F$ELEMENT(NUM,"/",FILE_LIST) $ IF FILE .EQS. "/" THEN GOTO DONE $ COPY 'FILE'.MEM MORRIS::DISK3:[DOCSET]*.* $ NUM = NUM + 1 $ GOTO PROCESS_LOOP $! $ DONE: $ WRITE SYS$OUTPUT "Finished copying files." $ EXIT
The first file returned by the F$ELEMENT lexical function is CHAP1, the
next file is CHAP2, and so on. Each time through the loop, the value of
NUM is increased so that the next file name is obtained. When the
F$ELEMENT returns a slash, all the items from FILE_LIST have been
processed and the loop is terminated.
14.20 Using the PIPE Command
The PIPE command executes one or more DCL command strings from the same command line. It enables you to perform UNIX-style command processing, such as command pipelining, input/output redirection, and conditional and background execution.
This style of command processing supports the development and use of Internet software, which often expects some form of pipeline command parsing to be present on both host and target systems.
The following sections describe different methods of using the PIPE command to execute DCL commands, how to interrupt a PIPE command, and how to improve subprocess performance. There are also examples.
For complete information about the PIPE command, refer to the OpenVMS DCL Dictionary: N--Z.
You can specify multiple DCL commands in a single PIPE command. The DCL commands are then executed sequentially. Use the following format:
PIPE command-sequence ; command-sequence [; command-sequences]...
14.20.1 Using the PIPE Command for Conditional Command Execution
A command sequence is executed conditionally depending on the execution result of the preceding command sequence. Use the format:
PIPE command-sequence1 && command-sequence2
Note that command-sequence2 executes if and only if command-sequence1 succeeds. If you use the following format, command-sequence2 executes if and only if command-sequence1 fails.
PIPE command-sequence1 || command-sequence2
|privacy and legal statement|