When the symbol CHANGE is true, the procedure transfers control to the label GET_CHANGE. Otherwise, the procedure executes the command following the IF command.
The following example illustrates two different IF commands:
$ COUNT = 0 $ LOOP: $ COUNT = COUNT + 1 $ IF COUNT .EQ. 9 THEN EXIT $ IF P'COUNT' .EQS. "" THEN EXIT . . . $ GOTO LOOP
The first IF command compares two integers; the second IF command compares two strings. Note that the .EQ. operator is used for the integer comparison and the .EQS. operator is used for the string comparison.
First, the value of COUNT is compared to the integer 9. When the values are equal, the procedure exits; when the values are not equal, the procedure continues. The loop terminates after eight parameters (the maximum number allowed) have been processed.
In the second IF expression, the string value of the symbol P'COUNT' is compared to a null string to see whether the symbol is undefined. Note that you must use apostrophes to force iterative substitution of the symbol COUNT. For example, when COUNT is 2, the result of the first translation is P2. Then, the value of P2 is used in the string comparison.
The following example executes the command procedure EXIT_ROUTINE.COM when the result of the IF expression is true:
$ GET_COMMAND_LOOP: $ INQUIRE COMMAND - "Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE)" $ IF COMMAND .EQS. "EXIT" THEN @EXIT_ROUTINE
The GOTO command passes control to a labeled line in a command procedure. (For additional information on label usage, refer to Chapter 15.) 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:
Step | Action |
---|---|
1 | Begin each possible starting point in the procedure with a label. |
2 |
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. |
3 |
At the beginning of the procedure, test the value of the symbol
$RESTART.
If $RESTART is true, execute a GOTO statement using the symbol BATCH$RESTART as the transfer label. |
$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 true. 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.
The following sections describe how to use tGOSUB and RETURN commands with subroutines.
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 15.) 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 15 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 16.3.
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:
$ CALL BAR $ $ MAIN: SUBROUTINE $ $ BAR: SUBROUTINE $ ENDSUBROUTINE $ $ ENDSUBROUTINE
$ 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.
The following sections describe how to write 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.
This 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:
Step | Action |
---|---|
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. |
3 |
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 15). However, you can also write loops that test the termination variable at the end of the loop, by following this procedure:
Step | Action |
---|---|
1 | Begin the loop. |
2 | Perform the commands in the body of the loop. |
3 | Change the termination variable. |
4 |
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, see the OpenVMS DCL Dictionary.
$ 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
$ 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.
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, see 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]...
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
A pipeline is a sequence of pipeline-segment commands connected by pipes, represented by the vertical-bar (|) separator. A pipeline-segment command is a DCL command that appears in a pipeline. The pipe connects the SYS$OUTPUT of one pipeline-segment command to the SYS$INPUT of the next command. The format of a pipeline is as follows:
PIPE pipeline-segment-command | pipeline-segment-command [|...]
Each pipeline-segment command runs in a separate subprocess with its SYS$OUTPUT connected to the SYS$INPUT of the next pipeline-segment command. These subprocesses execute in parallel; however, they are synchronized to the extent that each pipeline-segment command except the first reads the standard output of its predecessor as its standard input. A pipeline finishes execution when the last pipeline-segment command is done.
It is very common to use "filter applications" in a pipeline. A filter application is a program that takes data from SYS$INPUT, transforms it in a specific way, and writes it to SYS$OUTPUT.
Note that some aspects of DCL function differently in the context of a pipeline. For example:
$ ! TEE.COM - command procedure to display/log data flowing through $ ! a pipeline $ ! Usage: @TEE log-file $ $ OPEN/WRITE tee_file 'P1' $ LOOP: $ READ/END_OF_FILE=EXIT SYS$PIPE LINE $ WRITE SYS$OUTPUT LINE ! Send it out to the next stage of the pipeline $ WRITE tee_file LINE ! Log output to the log file $ GOTO LOOP $ EXIT: $ CLOSE tee_file $ EXIT
$ PIPE SHOW SYSTEM | @TEE showsys.log | SEARCH SYS$INPUT LEF
$ PIPE ... | (SET VERIFY=IMAGE ; ...) | ...
$ PIPE CC/NOOBJ/NOLIS TEST.C | SEARCH SYS$INPUT/WIND=(1,1) "%cc-w-" %SEARCH-F-RFAERR, RMS error using RFA access -RMS-F-RAC, invalid record access mode
A subshell is one or more command sequences separated by separators and enclosed in parentheses. The format of a subshell is as follows:
PIPE ( command-sequence [separator command-sequence]... )
6489P032.HTM OSSG Documentation 22-NOV-1996 13:17:24.78
Copyright © Digital Equipment Corporation 1996. All Rights Reserved.