OpenVMS
ユーザーズ・マニュアル


前へ 次へ 目次 索引


16.16.6 GOSUB と RETURN コマンドの使用

GOSUB コマンドは,コマンド・プロシージャの中のラベルの付いたサブルーチンに制御を渡します。コマンド・プロシージャの中にラベルがない場合には,実行は継続されず,強制終了されます(ラベルについての詳細は, 第 15 章 を参照してください)。 GOSUB コマンドは,プロシージャ・レベルあたり最大 16 までネストすることができます。

GOSUB コマンドは,ローカル・サブルーチン・コールの 1 つで,新しいプロシージャ・レベルは作成しません。このため,現在のコマンド・レベルで定義されているすべてのラベルとローカル・シンボルを GOSUB によって起動されたサブルーチンで使用できます。

RETURN コマンドは,サブルーチンを終了して, GOSUB コマンドの後のコマンドに制御を戻します。 RETURN コマンドで $STATUS の値を指定すると, DCL がサブルーチンの終了時に $STATUS に割り当てる値を無効にできます。この値は,0 〜 4 の整数か同値式でなければなりません。 $STATUS の値を指定すると,DCL はこの値を条件コードとして解釈します。 $STATUS の値を指定しないと,$STATUS の現在の値がセーブされます。

次の例は,GOSUB コマンドを使用して制御をサブルーチンに渡す方法を示しています。


$! 
$! 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)

例を確かめる場合は,次の点に注意してください。

  1. 最初の GOSUB コマンドは TEST1 のラベルが付いたサブルーチンに制御を渡す。

  2. プロシージャは,TEST1 サブルーチンの中のコマンドを実行して, TEST2 のラベルが付いたサブルーチンに分岐する。

  3. TEST1 サブルーチンの中の RETURN コマンドは,メイン・コマンド・プロシージャに制御を戻し,正しく実行されたことを示す 1 の値を $STATUS に渡す。

  4. TEST2 サブルーチンの中のRETURN コマンドは,TEST1 サブルーチンに制御を戻す。このコマンドは 3 番目のコマンドの前に実行されることに注意。

16.17 新しいコマンド・レベルの作成

新しいコマンド・レベルを作成するには,次の 2 種類の方法があります。

16.17.1 CALL コマンドの使用

CALL コマンドは,コマンド・プロシージャの中のラベルが付いたサブルーチンに制御を渡して,新しいコマンド・レベルを作成します。 CALL コマンドを使用すると,1 つのファイルの中に 2 つ以上の関連コマンド・プロシージャを保持できるため,プロシージャを管理しやすくなります。サブルーチン・ラベルは一意でなければならず,コマンド・プロシージャの中の CALL コマンドの前後に置きます。サブルーチン・ラベルの入力規則については, 第 15 章 を参照してください。

ラベルに加えて,サブルーチンには最大 8 個のオプション・パラメータを渡すことができます。パラメータについての詳細は, 第 16.2 節 を参照してください。

CALL コマンドを使用する場合には,次の規則に従ってください。

16.17.1.1 CALL コマンドの省力時の設定

CALL コマンドを使用する場合には,次の省略時の設定が使用されます。

16.17.1.2 サブルーチンの始まりと終わり

SUBROUTINE コマンドと ENDSUBROUTINE コマンドは, CALL サブルーチンの始まりと終わりを定義します。 SUBROUTINE コマンドのすぐ前にサブルーチンへのエントリ・ポイントを定義するラベルを置きます。 ENDSUBROUTINE コマンドのすぐ前に EXIT コマンドを置くことができますが,サブルーチンを終了する必要はありません。 ENDSUBROUTINE コマンドは,サブルーチンを終了して, CALL コマンドのすぐ後のコマンド行に制御を渡します。

サブルーチンの中のコマンド行が実行されるのは,サブルーチンをCALL コマンドで呼び出す場合だけです。コマンド・プロシージャを行ごとに実行する場合,コマンド言語インタプリタは, SUBROUTINEコマンドと ENDSUBROUTINE コマンドの間のすべてのコマンドをスキップします。

サブルーチン・エントリ・ポイントの有効範囲を定義したり,ラベル参照を使用するときには,次のような制限事項が適用されます。

次の例では,呼び出しは正しくありません。これは,CALL BAR コマンドが MAIN サブルーチンの外部に存在するからです。


$ CALL BAR 
$ 
$ MAIN: SUBROUTINE 
$ 
$     BAR: SUBROUTINE 
$     ENDSUBROUTINE 
$ 
$ ENDSUBROUTINE 

この CALL コマンドが正しく機能するようにするには,このコマンドを, SUBROUTINE と ENDSUBROUTINE で囲まれる部分に指定しなければなりません。

この例に示した呼び出しは,IF-THEN-ELSE ブロックの内部に存在するため,実行できません。


$ IF 1 
$ THEN 
$    BOB:SUBROUTINE 
$    ENDSUBROUTINE 
$ ENDIF 
$ CALL BOB 

次の例には,SUB 1 と SUB2 という 2 つのサブルーチンがあります。これらのサブルーチンは,CALL コマンドで呼び出されるまでは実行されません。


$ 
$! 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. 

CALL コマンドが SUB1 サブルーチンを起動して,出力をNAMES.LOG ファイルに切り換えます。 SUB1 サブルーチンは SUB2 サブルーチンを呼び出しています。プロシージャは SUB2 を実行して,プロシージャ実行(@) コマンドによってコマンド・プロシージャ FILE.COM を起動します。 SUB1 のすべてのコマンドが実行されると,メイン・プロシージャの中の CALL コマンドが SUB2 をもう一度呼び出します。 SUB2 の実行が終わると,プロシージャは終了します。

16.18 場合分け文の使用

場合分け文は,特殊な形式の条件コードであり,変数または式の値に応じて,一連のコマンド・ブロックの中の 1 つのブロックだけを実行します。普通,場合分け文で有効な値は,それぞれのコマンド・ブロックの先頭にあるラベルです。場合分け文は,指定された値を GOTO 文のターゲット・ラベルとして使用することによって,該当するコード・ブロックに制御を渡します。

場合分け文を作成するには,次のようにします。

  1. ラベルをリストする

  2. 場合分け文を作成する

  3. コマンド・ブロックを作成する

16.18.1 ラベルのリスト

ラベルをリストするには,スラッシュ(または,区切り文字として選択した文字)で区切られたラベルのリストが入っている文字列をシンボルに定義します。このシンボル定義はコマンド・ブロックの前に置かなくてはなりません。

この例は,COMMAND_LIST シンボルをラベル PURGE, DELETE,EXIT と同じにしています。


$ COMMAND_LIST = "/PURGE/DELETE/EXIT/" 

16.18.2 場合分け文の作成

場合分け文を作成するには,次の手順に従ってください。

手順 操作
1 INQUIRE コマンドを使用して場合分け変数の値を得る。
2 IF コマンドと一緒にF$LOCATE と F$LENGTH を使用して,場合分け変数の値が有効かどうかを判別する。
3 場合分け変数が有効な場合には,場合分け文(GOTO コマンド) を実行して,該当するコード・ブロックに制御を渡す。

場合分け変数が有効でない場合は,メッセージを表示して処理を終了するか,別の場合分け値を要求する。

次の例では,ラベルが完全なコマンド名に定義されています。したがって,F$LOCATE は,コマンド名の検索文字列の中に区切り文字を含めて,コマンドが短縮されないようにしています。


$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 

それぞれのコマンド・ブロックには,1 つ以上のコマンドを指定できます。コマンド・ブロックの先頭には固有のラベルを付けます。それぞれのコマンド・ブロックを終わるときには,コマンド・ブロックのリストにないラベルに制御を渡します。

コマンド・ブロックの作成

次の例では,それぞれのコマンド・ブロックに,1つ以上のコマンドを指定しています。コマンド・ブロックの先頭には固有のラベルを付けます (PURGE:, DELETE:)。それぞれのコマンド・ブロックを終わるときには,コマンド・ブロックのリストにないラベル(GOTO GET_COMMAND) に制御を渡します。


$GET_COMMAND: 
   .
   .
   .
$PURGE: 
$ INQUIRE FILE 
$ PURGE 'FILE' 
$ GOTO GET_COMMAND 
$ ! 
$DELETE: 
$ INQUIRE FILE 
$ DELETE 'FILE' 
$ GOTO GET_COMMAND 
$ ! 
$EXIT: 

16.19 ループの作成

コマンド・ブロックの先頭に,変数をテストするループを作成できます ( 第 15 章 を参照)。しかし,次の操作を実行すれば,ループの最後に終了変数をテストするループを作成することもできます。

手順 操作
1 ループを開始する。
2 ループ本体の中のコマンドを実行する。
3 終了変数を変更する。
4 終了変数をテストする。

条件が満足されない場合は,ループの始めに戻る。

5 ループを終了する。

ループの終わりにある終了変数をテストする場合は,終了変数の中の値にかかわらず,ループ本体のコマンドが少なくとも 1 回は実行されます。

次の 2 つの例は,COMMAND が "EX" (EXIT) に定義されたときに終了するループを実行します。F$EXTRACT は COMMAND を最初の 2 文字に切り捨てます。最初の例では,終了変数の COMMAND がループの始めにテストされ, 2 番目の例では,ループの終わりでテストされます。


$ ! 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 

ループを一定回数実行するには,終了変数としてカウンタを使用します。次の例では,ユーザによって入力された 10 個のファイル名がローカル・シンボル 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 
   .
   .
   .

次の例は,カウンタを使用してループの実行回数を制御します。この例では,ループは 10 回実行されます。終了変数はループの終わりにテストされます。


$! 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: 
   .
   .
   .

COUNT シンボルを使用してループの中のコマンドの実行回数を記録します。また,シンボル名 FILE_1,FILE_2 から FILE_10 までを作成するのにも COUNT を使用しています。 COUNT の値が増分されるのはループの始めですが,テストされるのはループの終わりであることに注意してください。したがって,COUNT が 9 から 10 に増分されると, IF 文が偽の結果を検出する前にループがもう 1 回だけ実行されます (FILE_10 の値を得ます)。

一連の値に対してループを実行するには,F$ELEMENT レキシカル関数を使用します。 F$ELEMENT 関数は,区切り文字によって区切られた項目リストから項目を取り出します。 F$ELEMENT の引数として項目番号,項目区切り文字,リストを指定しなければなりません。

F$ELEMENT レキシカル関数の使用方法についての詳細は,『Compaq OpenVMS DCL ディクショナリ』を参照してください。

次の例では,ファイル CHAP1,CHAP2,CHAP3,CHAPA,CHAPB,CHAPC が順に処理されます。


$ 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 シンボルにリストされたファイルを別のノードのディレクトリにコピーします。


$ 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 

F$ELEMENT レキシカル関数から戻される最初のファイルは CHAP1 であり,次のファイルは CHAP2 です。以下も同様です。ループを実行するたびに,NUM の値が増分されるため,次のファイル名が入手されます。 F$ELEMENT がスラッシュを戻した場合には, FILE_LIST のすべての項目が処理されたため,ループは終了します。

16.20 PIPE コマンドの使用

PIPE コマンドを使用すると,同一コマンド行から 1 つまたは複数の DCL コマンドを実行できます。これにより,コマンドのパイプライン処理,入出力のリダイレクト,条件実行,バックグラウンド処理など,UNIX 形式のコマンドを処理できるようになります。

このようなコマンド処理の形式は,インターネット・ソフトウェアの開発や使用をサポートします。インターネット・ソフトウェアでは,ホスト・システムとターゲット・システムの両方で存在を解析する,パイプライン・コマンドの使用を前提にしています。

これ以降の節では,PIPE コマンドへの割り込み方法,サブプロセスの性能を向上させる方法など, DCL コマンドの実行以外の目的で PIPE コマンドを使用する方法を説明します。

PIPE コマンドについての詳細は,『Compaq OpenVMS DCL ディクショナリ: N--Z』を参照してください。

1 つの PIPE コマンドに,複数の DCL コマンドを指定できます。指定した DCL コマンドは,順番に実行されます。次の形式を使用します。


PIPE   command-sequence ; command-sequence [; command-sequences]... 


前へ 次へ 目次 索引