[ 前のページ ] [ 次のページ ] [ 目次 ] [ 索引 ] [ DOC Home ]

16 DCLでの拡張プログラミング

拡張DCLプログラミングとは,複雑なコマノド・プロシージャとDCLコマンドのPIPE コマンドを使用することです。

複雑なコマンド・プロシージャは,プログラムと同様の機能を実行できます。 コマンド・プロシージャでは,変数入力を使用でき,特定の条件が真のときにだけプロシージャのセクションを実行でき, サブルーチンを実行したり, 他のコマンド・プロシージャを起動することができます。

DCLのPIPEコマンドも,プログラムと同様の機能を実行できます。たとえばPIPE コマンドを使用すると,同一DCLコマンド行から,次に示す1つまたは複数の操作を実行できます。

本章では,次のことについて説明します。

本章は次の方を対象にしています。

その他の情報については,次のものを参照してください。

16.1 コマンド・プロシージャ入力の実行

コマンド・プロシージャでは,ユーザが提供するデータを必要とすることがよくあります。 このデータ,つまり入力は会話形式で入手でき(第15章を参照), 非会話形式で入手することもできます。 本章では,非会話型入力方式について説明し,第15章で説明していない会話型入力方式についても説明します。

コマンド・プロシージャを実行するたびに,同じデータを使用できます。 同じデータを使用するには,コマンド・プロシージャでデータを必要とするコマンドの後のデータ行にデータを指定します。

次のコマンド・プロシージャは,CENSUS.EXEコマンド・プロシージャを実行します。CENSUS.EXE はプロシージャが実行されるたびに 1993,1994, 1995のデータを読み込みます。

     $ ! CENSUS.COM
     $ !
     $ RUN CENSUS
     1993
     1994
     1995
     $ EXIT

16.1.1 コマンド・プロシージャにデータを登録する場合の制限事項

DCLは,データ行に指定されたテキストをコマンド・プロシージャに直接渡します。 したがって,次に示すように,変換しなければならないデータは処理されません。

16.1.2 その他のデータ入力方式

この後の節では,次に示すように,コマンド・プロシージャに対する入力データを入手するための別の方法について説明します。

16.2 データを渡すためのパラメータの使用

コマンド・プロシージャにデータとしてパラメータを渡す場合には,次のガイドラインに従ってください。

DCLはローカル・シンボルP1〜P8を使用して,パラメータをコマンド・プロシージャに渡します。P1 には最初のパラメータ値が割り当てられ,P2には2 番目のパラメータ値,P3には3番目のパラメータ値が割り当てられます。 以下も同様です。たとえば,次のコマンドは,コマンド・プロシージャSUM.COM を起動し,8つのパラメータをプロシージャに渡します。

     $ @SUM 34 52 664 89 2 72 87 3

16.2.1 整数としてのパラメータの指定

パラメータとして整数を指定した場合には,それは文字列に変換されます。 次の例では,P1は文字列値24であり,P2は文字列値25です。

     $ @ADDER 24 25

シンボルP1〜P8は,整数式と文字列式の両方で使用できます。DCLは必要な変換を自動的に実行します。

16.2.2 文字列としてのパラメータの指定

文字列内のスペース,タブ,小文字を保存するには,文字列の前後を引用符(" ") で囲みます。次の例を参照してください。

     $ @DATA "Paul Cramer"

次の例では,P1はPaul Cramerであり,P2はヌルです。引用符を省略した場合には, 各文字列は独立したパラメータとして渡されます。次の例を参照してください。

     $ @DATA Paul Cramer

この例では,文字列PaulとCramerが大文字に変換され,P1はPAULに,P2はCRAMER になります。

もう1つの例として,次のコマンドでDATA.COMを起動した場合を考えます。

     $ @DATA  "Paul Cramer" 24 "(555) 111-1111")

P1〜P8はDATA.COMの中では次のように定義されています。


P1 = Paul Cramer
P2 = 24
P3 = (555) 111-1111
P4-P8 = null

16.2.3 シンボルとしてのパラメータの指定

シンボル値を渡すには,シンボルの前後を一重引用符で囲みます。シンボル値の内部でスペース, タブ,小文字を保存するには,値を3組の引用符で囲みます。 文字列の一部として引用符を使用する場合には,3組の引用符を使用しなければなりません。

別の方法として,テキストを引用符で囲み,シンボルが表示されるときに, シンボルの前に2つの一重引用符を指定し,シンボルの後に1つの一重引用符を指定する方法があります。

次の例では,P1はPaulであり,P2はCramerです。シンボルをコマンド・プロシージャに渡すときに,DCL は引用符を削除します。

     $ NAME = "Paul Cramer"
     $ @DATA 'NAME'

次の例では,P1は"Paul Cramer"であり,P2はヌルです。

     $ NEW_NAME = """Paul Cramer"""
     $ @DATA 'NEW_NAME'

次の例では,P1はPaul Cramerに変換されます。

     $ ! DATA.COM
     $ @NAME "''P1'"

16.2.4 ヌル値としてのパラメータの指定

空のパラメータを渡すには,コマンド文字列の位置を表す一組の二重引用符を使用します。 次の例では,DATA.COMに渡される最初のパラメータは空のパラメータです。

     $ @DATA "" "Paul Cramer"

この例では,P1が空値で,P2がPaul Cramerです。

16.3 バッチ・ジョブにデータを渡すためのパラメータの使用

バッチ・モードで実行されるコマンド・プロシージャにパラメータを渡すには,SUBMIT コマンドの修飾子/PARAMETERSを使用します。

1つのSUBMITコマンドを使用して,複数のコマンド・プロシージャを実行する場合には, 指定したパラメータは,バッチ・ジョブ内の各コマンド・ プロシージャに対して使用されます。

次の例では,コマンドは,3つのパラメータをコマンド・プロシージャASK.COM とGO.COMに渡します。これらのコマンド・プロシージャはバッチ・ ジョブとして実行されます。

     $ SUBMIT/PARAMETERS=(TODAY,TOMORROW,YESTERDAY) ASK.COM, GO.COM)

次の例では,SUBMITコマンドは,2つのパラメータをコマンド・プロシージャLIBRARY.COM とSORT.COMに渡します。

     $ SUBMIT-
     _$ /PARAMETERS=(DISK:[ACCOUNT.BILLS]DATA.DAT,DISK:[ACCOUNT]NAME.DAT) -
     _$ LIBRARY.COM, SORT.COM

ユーザがログインして,各コマンド・プロシージャを実行したかのように, バッチ・ジョブが実行されます。このSUBMITコマンドが実行するバッチ・ ジョブは,ユーザのアカウントにログインし,ユーザのログイン・コマンド・ プロシージャを実行し,その後,次のコマンドを実行します。

     $ @LIBRARY DISK:[ACCOUNT.BILLS]DATA.DAT DISK:[ACCOUNT]NAME.DAT)
     $ @SORT DISK:[ACCOUNT.BILLS]DATA.DAT DISK:[ACCOUNT]NAME.DAT)

データをコマンド・プロシージャの中に指定する方法,SYS$INPUTをファイルとして定義する方法でも, データをバッチ・ジョブに渡すことができます。 指定されたパラメータは,バッチ・ジョブのそれぞれのコマンド・ プロシージャで使用されます。

16.4 ネスティングしたコマンド・プロシージャにデータを渡すためのパラメータの使用

ネスティングしたコマンド・プロシージャに最大8つのパラメータを渡すことができます。 ネスティングしたプロシージャでのローカル・シンボルP1 〜P8は,起動するプロシージャのローカル・シンボルP1〜P8と関連づけられません。

次の例では,DATA.COMは,ネスティングしたコマンド・プロシージャNAME.COM を起動します。

     $ ! DATA.COM
     $ @NAME 'P1' Joe Cooper

DATA.COMのP1がPaul Cramerという文字列である場合には,この文字列引用符が含まれていないため,2 つのパラメータとしてNAME.COMに渡されます。NAME.COM では,P1〜P8は次のように定義されます。


P1 = PAUL
P2 = CRAMER
P3 = JOE
P4 = COOPER
P5-P8 = null

DATA.COMのP1が"Paul Cramer" (引用符が含まれている)の場合には,次に示すように,3 組の引用符でP1を囲むことにより,1つのパラメータとして値をNAME.COM に渡すことができます。

     $ ! DATA.COM
     $ QUOTE = """
     $ P1 = QUOTE + P1 + QUOTE
     $ @NAME 'P1' "Joe Cooper"

この例では,コマンド・プロシージャNAME.COMでP1はPaul Cramerであり,P2 はJoe Cooperです。

16.5 データを要求するプロンプトの表示

コマンド・プロシージャのデータを会話形式で入手するには,INQUIREコマンド( 第15章を参照)またはREADコマンドを使用できます。 どちらのコマンドも,入力を要求するプロンプトを表示し, 応答をシンボルに割り当てます。

READコマンドとINQUIREコマンドの相違点は次のとおりです。

INQUIREコマンド READコマンド
値を要求するプロンプト 値を要求するプロンプト
ターミナルから値を読み込む 最初のパラメータによって指定されたソースから値を読み込む
値をシンボルに割り当てる 2番目のパラメータとして指定されたシンボルに値を割り当てる。

READコマンドは,プロンプトに対する応答としてターミナルに入力されたすべての文字を, 正確な文字列値として受け付けます(大文字と小文字の区別, スペース,タブは保存されます)。/PROMPT修飾子を省略した場合には,READ コマンドは省略時のプロンプトとしてData:を表示します。

入力されたパラメータを受け付けることができ,また,必須パラメータが指定されなかったときに, ユーザ入力を要求するプロンプトを表示するコマンド・ プロシージャを作成することもできます。

次の例では,コマンドはFilename:というプロンプトをターミナルに表示し, 論理名SYS$COMMAND (省略時の設定ではターミナル)によって指定されるソースから応答を読み込み, 応答をシンボルFILEに割り当てます。

     $ READ/PROMPT="Filename: "  SYS$COMMAND  FILE

次の例では,プロシージャを起動したときに,ファイル名が指定されなかった場合には, ファイル名を要求するプロンプトが表示されます。

     $ ! Prompt for a file name if name
     $ ! is not passed as a parameter
     $ IF P1 .EQS. "" THEN INQUIRE P1 "Filename"
     $ COPY 'P1' DISK5:[RESERVED]*.*
     $ EXIT


注意
コマンド・プロシージャをキューに登録して, バッチ・ジョブとして実行する場合,DCLは,INQUIREコマンドに続くデータ行からINQUIRE コマンドに指定されたシンボルの値を読み込みます。 データ行を指定しない場合には,シンボルには空値が割り当てられます。

16.6 データを入手するためのSYS$INPUT論理名の使用

コマンド,ユーティリティ,その他のシステム・イメージは,省略時の入力ストリームである論理名SYS$INPUT に指定されるソースから入力を取得します。 コマンド・プロシージャの中では,SYS$INPUTはコマンド・プロシージャ・ ファイルとして定義され,データを必要とするコマンドやイメージはこのファイルの中でデータ行を検索します。 ただし,SYS$INPUTを再定義すれば, ターミナルや別の入力ファイルからデータを得ることができます。

16.6.1 ターミナルとしてのSYS$INPUTの再定義

SYS$INPUTをターミナルとして再定義できます。このようにすると,コマンド・ プロシージャから呼び出されたイメージは,コマンド・プロシージャ内のデータ行からではなく, 会話形式で入力を入手できます。

コマンド・プロシージャで会話型入力を必要とするDCLコマンドやユーティリティを使用する場合には,SYS$INPUT をターミナルとして再定義しなければなりません。

たとえば,次のコマンド・プロシージャは,入力を会話形式でCENSUS.EXE イメージに提供できるようにします。

     $ ! Execute CENSUS getting data from the terminal
     $ DEFINE/USER_MODE SYS$INPUT SYS$COMMAND
     $ RUN CENSUS
     $ EXIT

DEFINE/USER_MODEコマンドは,CENSUS.EXEを実行しながら,SYS$INPUTを一時的に再定義するため,CENSUS.EXE はターミナルから入力を受け入れます。CENSUS.EXE が終了すると,SYS$INPUTは元の定義(コマンド・プロシージャ・ ファイル)に戻されます。

たとえば,次のコマンド・プロシージャはEVEエディタを使用します。

     $ ! Obtain a list of your files
     $ DIRECTORY
     $ !
     $ ! Get file name and invoke the EVE editor
     $ EDIT_LOOP:
     $      INQUIRE FILE "File to edit (Press Return to end)"
     $      IF FILE .EQS. "" THEN EXIT
     $      DEFINE/USER_MODE SYS$INPUT SYS$COMMAND
     $      EDIT/TPU 'FILE'
     $      GOTO EDIT_LOOP

このコマンド・プロシージャは,ユーザがReturnキーを押してループを終了するまで, ファイル名を求めるプロンプトを出します。ファイル名を入力すると, プロシージャは自動的にEVEエディタを起動してファイルを編集します。 エディタの実行中,SYS$INPUTはターミナルとして定義されるので, 編集内容を会話形式で入力できます。

16.6.2 SYS$INPUTを個別ファイルとして定義する場合

SYS$INPUTをファイルとして定義すれば,コマンド・プロシージャはファイルから入力を得ることができます。 コマンド・プロシージャは,データ行のテキストをコマンドまたはイメージに直接渡すことに注意してください。DCL はデータ行は処理しません。データ行にDCLシンボルや式を指定しても,DCL は値をシンボルに置換しませんし,式を評価しません。データ行で感嘆符を使用すると, データを渡す先のイメージが感嘆符(!)を処理します。

データ・ファイルの名前をSYS$INPUTとして指定すれば,コマンド・プロシージャ・ ファイルにプログラムを登録できます。この操作を実行すると, コンパイラは別のファイルではなく,コマンド・プロシージャからプログラムを読み込みます。

次の例は,FORTRANコマンドの後にプログラムの文が続くコマンド・プロシージャを示しています。

     $ FORTRAN/OBJECT=TESTER/LIST=TESTER SYS$INPUT
     C  THIS IS A TEST PROGRAM
        A = 1
        B = 2
        STOP
        END
     $ PRINT TESTER.LIS
     $ EXIT

FORTRANコマンドは,論理名SYS$INPUTを使用してコンパイルするファイルを識別します。SYS$INPUT はコマンド・プロシージャとして定義されているため,FORTRAN コンパイラは,FORTRANコマンドに続く文を(始めにドル記号が付く次の行まで) コンパイルします。コンパイルが終了すると,2 つの出力ファイルTESTER.OBJと TESTER.LISが作成されます。このとき, PRINTコマンドは,リスト・ファイルを印刷します。

16.7 コマンド・プロシージャ出力の実行

データやエラー・メッセージ,コマンド行のチェックなど,コマンド・プロシージャからの出力は, ターミナルまたは他のファイルに送信できます。 この節では,次の出力方法について説明します。

16.7.1 ターミナルへのデータの書き込み

シンボルやレキシカル関数を含むデータを書き込むには,WRITEコマンドを使用します。 データを二重引用符(" ")で囲む場合を除いて,WRITEコマンドは自動的にシンボル置換を実行します。

次の例では,SYS$INPUTはデータ・ファイルとして指定されています。 TYPEコマンドは,その後に続くデータ行からデータを読み込み,データ行をターミナルに表示します。

     $ ! Using TYPE to display lines
     $ TYPE SYS$INPUT
     REPORT BY MARY JONES
     PREPARED APRIL 15, 1995
     SUBJECT: Analysis of Tax Deductions for 1995
        .
        .
        .
     $ EXIT

シンボルやレキシカル関数を含むデータを書き込むには,WRITEコマンドを使用します。 データを二重引用符(" ")で囲む場合を除いて,WRITEコマンドは自動的にシンボル置換を実行します。

WRITEコマンドを使用して,文字列をリテラル・テキストとして表示するには, 二重引用符(" ")で囲みます。たとえば,次のようになります。

     $ WRITE SYS$OUTPUT "Two files are written."
     Two files are written.

文字列の中に二重引用符を指定するには,2組の二重引用符("" "")を使用します。 たとえば,次のようになります。

     $ WRITE SYS$OUTPUT "Summary of ""Q & A"" Session"
     Summary of "Q & A" Session

テキスト行を2行以上に継続するには,2つの文字列をプラス記号(+)とハイフン(-) で連結します。たとえば,次のようになります。

     $ WRITE SYS$OUTPUT "Report by Mary Jones" + -
     " Prepared April 15, 1996"
     Report by Mary Jones Prepared April 15, 1996

WRITEコマンドは,シンボル置換を自動的に実行し,シンボルの値を表示します。 文字列内でシンボル置換を強制的に実行するには,シンボルを一重引用符で囲みます。 たとえば,次のようになります。

     $ AFILE = "STAT1.DAT"
     $ BFILE = "STAT2.DAT"
     $ WRITE SYS$OUTPUT "''AFILE' and ''BFILE' ready."
     STAT1.DAT and STAT2.DAT ready.

この例では,STAT1.DATがAFILEシンボルの変換結果で,STAT2.DATがBFILE シンボルの変換結果になっています。

16.7.2 コマンドとイメージからの出力先の切り換え

コマンド,ユーティリティ,その他のシステム・イメージは,論理名SYS$OUTPUT によって指定されるソースに出力を書き込みます。省略時の設定では,SYS$OUTPUT はターミナルに定義されます。ただし,次のいずれかの方法を使用すれば, 出力先を切り換えることができます。

次の例では,コマンド・プロシージャは,SHOW USERSコマンドからの出力をファイルに切り換えます。SYS$OUTPUT の新しい定義は,SHOW USERSコマンドの実行に対してだけ有効になります。

     $ DEFINE/USER_MODE SYS$OUTPUT SHOW_USER.DAT
     $ SHOW USERS
     $ !
     $ ! Process the information in SHOW_USER.DAT
     $ OPEN/READ INFILE SHOW_USER.DAT
     $ READ INFILE RECORD
        .
        .
        .
     $ CLOSE INFILE
     $ EXIT

次の例では,SYS$OUTPUTはヌル・デバイス(NL:)として定義されています。

     $ DEFINE/USER_MODE SYS$OUTPUT NL:
     $ APPEND NEW_DATA.DAT STATS.DAT
        .
        .
        .

/USER_MODE修飾子は,次のイメージが完了するまでの間だけ有効な一時的な論理名割り当てを作成します。 このコマンドを実行すると,SYS$OUTPUT は省略時の定義(ターミナル)に戻ります。

DEFINE/USER_MODEコマンドを使用しても,コマンド・インタプリタの中で実行されるDCL コマンドの出力先を切り換えることはできません。この場合には,DEFINE コマンドを使用してSYS$OUTPUTを再定義し,処理が終了したら,DEASSIGN コマンドを使用して定義を削除します。

次の表は,コマンド・インタプリタの内部で実行されるDCLコマンドを示しています。

= ALLOCATE ASSIGN
ATTACH CALL CANCEL
CLOSE CONNECT CONTINUE
CREATE/LOGICAL_NAME_ TABLE DEALLOCATE DEASSIGN
DEBUG DECK DEFINE
DEFINE/KEY DELETE/SYMBOL DISCONNECT
ELSE ENDIF ENDSUBROUTINE
EOD EXAMINE EXIT
GOSUB GOTO IF
INQUIRE ON OPEN
READ RECALL RETURN
SET CONTROL SET DEFAULT SET KEY
SET ON SET OUTPUT_RATE SET PROMPT
SET PROTECTION/DEFAULT SET SYMBOL/SCOPE SET UIC
SET VERIFY SHOW DEFAULT SHOW KEY
SHOW PROTECTION SHOW QUOTA SHOW STATUS
SHOW SYMBOL SHOW TIME SHOW TRANSLATION
SPAWN STOP SUBROUTINE
THEN WAIT WRITE

次の例では,SHOW TIMEコマンドからの出力をファイルTIME.DATに切り換えるために使用されるコマンドを示しています。SYS$OUTPUT の割り当てを解除した後, 省略時の定義(ターミナル)に戻ります。

     $ DEFINE SYS$OUTPUT TIME.DAT
     $ SHOW TIME
     $ DEASSIGN SYS$OUTPUT

16.7.3 コマンド・プロシージャからのデータを戻す

グローバル・シンボルと論理名は,コマンド・プロシージャのデータを呼び出し側プロシージャまたはDCL コマンド・レベルに戻します。グローバル・ シンボルや論理名は,どのコマンド・レベルでも読み込めます。論理名は, ネストされたコマンド・プロシージャから呼び出し側のプロシージャにデータを戻すことができます。

次の例では,グローバル割り当て文で作成されたグローバル・シンボルを使用して, コマンド・プロシージャが値を渡す方法を示しています。

     $ @DATA "Paul Cramer"

     $ ! DATA.COM
     $ !
     $ ! P1 is a full name.
     $ ! NAME.COM returns the last name in the
     $ ! global symbol LAST_NAME.
     $ !
     $ @NAME 'P1'
            $ ! NAME.COM
            $ ! P1 is a first name
            $ ! P2 is a last name
            $ ! return P2 in the global symbol LAST_NAME
            $ LAST_NAME == P2
            $ EXIT
     $ ! write LAST_NAME to the terminal
     $ WRITE SYS$OUTPUT "LAST_NAME = ''LAST_NAME'"

     LAST_NAME = CRAMER

DATA.COMは,コマンド・プロシージャNAME.COMを起動し,NAME.COMに姓名を渡します。NAME.COM はグローバル・シンボルLAST_NAMEに姓を格納します。NAME.COM が終了すると,DCLはDATA.COMの実行を続行し,このコマンド・ プロシージャは,グローバル・シンボルLAST_NAMEを指定することにより, 姓を読み込みます。コマンド・プロシージャNAME.COMは別のファイルに格納されています。 この例では,わかりやすくするためにインデントを使用して示しています。

このコマンド・プロシージャでは,REPORT.COMはレポートのファイル名を入手し, ファイル名を論理名REPORT_FILEに等しく定義し,レポートをREPORT_FILE に書き込むプログラムを実行します。

     $! Obtain the name of a file and then run
     $! REPORT.EXE to write a report to the file
     $!
     $ INQUIRE FILE "Name of report file"
     $ DEFINE/NOLOG REPORT_FILE 'FILE'
     $ RUN REPORT
     $ EXIT

次の例では,コマンド・プロシージャREPORT.COMは別のプロシージャから起動されます。 呼び出し側のプロシージャは,論理名REPORT_FILEを使用して, レポート・ファイルを参照します。

     $! Command procedure that updates data files
     $! and optionally prepares reports
     $!
     $ UPDATE:
        .
        .
        .
     $   INQUIRE REPORT "Prepare a report [Y or N]"
     $   IF REPORT THEN GOTO REPORT_SEC
     $   EXIT
     $!
     $ REPORT_SEC:
     $   @REPORT
     $   WRITE SYS$OUTPUT "Report written to ", F$TRNLNM("REPORT_FILE")
     $   EXIT

16.7.4 エラー・メッセージの出力先の切り換え

これ以降の節では,エラー・メッセージの出力先を切り換える方法を説明します。

16.7.4.1 SYS$ERRORの再定義

省略時の設定では,コマンド・プロシージャは,SYS$ERRORに示されるファイルにシステム・ エラー・メッセージを送信します。SYS$ERRORを再定義すれば, エラー・メッセージを指定するファイルに書き出すこともできます。 ただし,SYS$ERRORをSYS$OUTPUTと異なるように再定義すると(SYS$ERROR を再定義せずにSYS$OUTPUTを再定義すると),標準VMSシステム・ エラー表示方式を使用するDCLコマンドやイメージは,システム・ エラー・メッセージとシステム重大エラー・メッセージをSYS$ERRORとSYS$OUTPUT の両方に送信します。したがって,ユーザはこれらのメッセージを,SYS$ERROR の定義に示されるファイルで1回と,SYS$OUTPUTに示されるファイルで1 回の合計2回受信することになります。成功メッセージ,通知メッセージ, 警告メッセージは,SYS$OUTPUTに示されるファイルにだけ送信されます。

DCLコマンドからエラー・メッセージが出されないようにしたい場合は, SYS$ERRORもSYS$OUTPUTもターミナルに定義しないようにします。

コマンド・プロシージャから自分のイメージを実行するときに,そのイメージでSYS$ERROR を参照する場合には,イメージはシステム・エラー・ メッセージを,SYS$ERRORによって示されるファイルにだけ送信します。 SYS$ERRORがSYS$OUTPUTと異なる場合でも,この規則が適用されます。 SYS$ERRORとSYS$OUTPUTが異なる場合には,標準的なシステム・エラー表示メカニズムを使用するDCL コマンドとイメージだけが,この2つのファイルにメッセージを送信します。

このコマンド・プロシージャはパラメータとしてディレクトリ名を受け付け, 省略時の値をそのディレクトリに設定し,ディレクトリ内のファイルをパージします。 システム・エラー・メッセージが出力されないようにするために, プロシージャは,SYS$ERRORとSYS$OUTPUTをヌル・デバイスとして一時的に定義します。

     $ ! Purge files in a directory and suppress messages
     $ !
     $ SET DEFAULT 'P1'
     $ ! Suppress messages
     $ !
     $ DEFINE/USER_MODE SYS$ERROR NL:
     $ DEFINE/USER_MODE SYS$OUTPUT NL:
     $ PURGE
     $ EXIT

16.7.4.2 システム・エラー・メッセージの禁止

システム・メッセージが出力されないようにするために,SET MESSAGEコマンドを使用することもできます。/NOFACILITY ,/NOIDENTIFICATION, /NOSEVERITY,/NOTEXTのいずれかの修飾子を使用すれば,機能名,メッセージ識別, 重大度レベル,メッセージ・テキストを出力しないように設定できます。

次の例では,2番目のSET MESSAGEコマンドが実行されるまで,機能,識別, 重大度,テキスト・メッセージの出力が一時的に禁止されます。

     $ ! Purge files in a directory and suppress system messages
     $ !
     $ SET DEFAULT 'P1'
     $ ! Suppress system messages
     $ !
     $ SET MESSAGE/NOFACILITY -
                  /NOIDENTIFICATION -
                  /NOSEVERITY -
                  /NOTEXT
     $ PURGE
     $ SET MESSAGE/FACILITY -
                  /IDENTIFICATION -
                  /SEVERITY
                  /TEXT
     $ EXIT

16.8 ファイルの読み込みと書き込み(ファイル入出力)

コマンド・プロシージャからファイルを読み込んだり,コマンド・プロシージャにファイルを書き込んだりするには, 次のようにします。

手順 操作
1 OPENコマンドを使用してファイルをオープンする。

OPENコマンドは,論理名をファイルに割り当て,ファイルを読み込むのか, 書き込むのか,または読み込みと書き込みを両方行うのかを指定する。 これ以降のREAD,WRITE,CLOSEコマンドは,この論理名を使用してファイルを参照する。

2 READ またはWRITEコマンドを使用して,ファイルからレコードを読み込んだり, ファイルにレコードを書き込んだりする。

ファイルへの入出力を行う1つの方法に,レコードを読み込んだり,レコードを処理したり, 変更済みのレコードを同じファイルまたは別のファイルに書き込んだりするループを設計する方法がある。

3 CLOSEコマンドを使用してファイルをクローズする。

CLOSEコマンドを取り込まない場合,ログアウトするまでファイルがオープンされたままになる。


注意
ユーザがログインすると,システムがSYS$INPUT ,SYS$OUTPUT,SYS$COMMAND,SYS$ERRORの各ファイルを自動的にオープンするため, これらのファイルを読み書きのために明示的にオープンする必要はありません。

この後の節では,次のことについて説明します。

16.9 OPENコマンドの使用

OPENコマンドは,順編成ファイル,相対編成ファイル,索引順編成ファイルをオープンします。 ファイルはプロセス永久ファイルとしてオープンされ,CLOSE コマンドでファイルを明示的にクローズしない限り,プロセスが存在する間はオープンされたままになります。 ファイルがオープンされているときは, プロセス永久ファイルの使用に関するOpenVMS RMSの制限事項に従います。

OPENコマンドは,ファイルをオープンすると,論理名(最初のパラメータとして指定される) をファイル(2番目のパラメータとして指定される)に割り当て, その名前をプロセス論理名テーブルに収めます。これ以降のREAD ,WRITE,CLOSEコマンドはこの論理名を使用してファイルを参照します。

次の例では,OPENコマンドは,論理名INFILEをDISK4:[MURPHY]STATS.DAT ファイルに割り当てます。

     $ OPEN/READ INFILE DISK4:[MURPHY]STATS.DAT


注意
OPENコマンドの中の論理名は固有の名前でなければなりません。 入力したコマンドは正しいのに,OPEN コマンドが動作しない場合には,OPENコマンドの中の論理名を変更します。 論理名定義のリストを表示するには,SHOW LOGICALコマンドを使用します。

コマンド・プロシージャが正しいファイルにアクセスできるようにするには, 完全なファイル指定(たとえば,DISK4:[MURPHY]STATS.DAT)を使用するか, ファイルをオープンする前にSET DEFAULTコマンドを使用して正しいデバイスとディレクトリを指定します。

共用可能ファイルも指定できます。/SHARE修飾子を使用すると,他の開かれているファイルをアクセス可能に設定できます。 さらに,ユーザはDCL コマンドTYPEとSEARCHを使用して,共用可能ファイルにアクセスできます。

OPEN/READコマンドはファイルを開き,論理名をファイルに割り当て,レコード・ ポインタをファイルの先頭に設定します。読み込みの対象としてファイルを開く場合には, レコードを読み込むことはできますが,書き込むことはできません。 レコードを読み込むたびに,ポインタは次のレコードに移動します。

このコマンド・プロシージャのOPEN/READコマンドは,ファイルSTATS.DAT を開き,論理名INFILEをファイルに割り当てます。

     $   OPEN/READ INFILE DISK4:[MURPHY]STATS.DAT
     $ READ_FILE:
     $   READ/END_OF_FILE=DONE INFILE DATA
     $   GOTO READ_FILE
     $ DONE:
     $   CLOSE INFILE
     $   EXIT

新しいファイルに書き込む場合には,OPEN/WRITEコマンドを使用します。 OPEN/WRITEコマンドは印刷ファイル形式で順編成ファイルを作成します。 ファイルのレコード形式は固定長制御部付き可変長(VFC)であり,2バイトのレコード・ ヘッダが付いています。/WRITE修飾子を/APPEND修飾子と組み合わせて使用することはできません。

既存のファイルを指定した場合には,OPEN/WRITEコマンドは,既存のファイルより1 つだけバージョン番号の大きい新しいファイルを開きます。

次の例のコマンド・プロシージャは,書き込みのために使用できる新しいファイル(NAMES.DAT) を作成します。

     $   OPEN/WRITE OUTFILE DISK4:[MURPHY]NAMES.DAT
     $ UPDATE:
     $   INQUIRE NEW_RECORD "Enter name"
     $   WRITE OUTFILE NEW_RECORD
     $   IF NEW_RECORD .EQS. "" THEN GOTO EXIT_CODE
     $   GOTO UPDATE
     $ EXIT_CODE:
     $   CLOSE OUTFILE
     $   EXIT

OPEN/APPENDコマンドは,既存のファイルの最後にレコードを追加します。 存在しないファイルを開こうとした場合には,エラーが発生し,ファイルは開かれません。/APPEND 修飾子を/WRITE修飾子と組み合わせて使用することはできません。

次の例では,レコードが既存のファイル,NAMES.DATの最後に追加されます。

     $ OPEN/APPEND OUTFILE DISK4:[MURPHY]NAMES.DAT
     $ INQUIRE NEW_RECORD "Enter name"
     $ WRITE OUTFILE NEW_RECORD
        .
        .
        .
     $ CLOSE OUTFILE

OPEN/READ/WRITEコマンドは,レコード・ポインタをファイルの先頭に設定し, 最初のレコードを読み込むことができるようにします。この方法を使用してファイルを開く場合には, 最後に読み込んだレコードだけを置換できます。 新しいレコードをファイルの最後に書き込むことはできません。 さらに,変更されたレコードは,置換されるレコードと正確に同じサイズでなければなりません。

次の例では,レコード・ポインタがファイルSTATS.DATの先頭に設定され, 最初のレコードを読み込むことができるようにします。

     $ OPEN/READ/WRITE FILE DISK4:[MURPHY]STATS.DAT

16.10 ファイルへの書き込み

ファイルにデータを書き込むには,次のようにします。

手順 操作
1 書き込み用のファイルをオープンする。
2 書き込みループの先頭にラベルを付ける。

単独のレコードを書き込んだり読み込んだりする場合を除いて,ファイル入出力は常にループの中で実行される。

3 書き込もうとするデータを読み込む。

INQUIREコマンドまたはREADコマンドを使用して,データをシンボルに読み込む。

4 データをテストする。

データが入ってるシンボルをチェックする。シンボルが空の場合(たとえば,Return を押して当該行にデータを入力しない場合)には,ファイルに書き込もうとするデータの終わりに達したら, ループの終わりに進む。 それ以外の場合には,処理を続ける。

5 データをファイルへ書き込む。

WRITEコマンドを使用して,シンボルの値(1つのレコード)をファイルに書き込む。

6 ループの先頭に戻る。

以下,ファイルに書き込むデータがなくなるまで,ループが繰り返される。

7 ループを終了して, ファイルをクローズする。

次のコマンド・プロシージャは,データを新しいファイルSTATS.DATに書き込みます。 この名前のファイルが存在する場合には,新しいバージョンが作成されます。

     $ ! Write a file
     $ ON ERROR THEN EXIT                         ! Exit if the command
     $ !                                          !   procedure cannot
     $ !                                          !   open the file
     $ OPEN/WRITE IN_FILE DISK4:[MURPHY]STATS.DAT ! Open the file
     $ ON CONTROL_Y THEN GOTO END_WRITE           ! Close the file if you
     $ !                                          !   quit execution with
     $ !                                          !   Ctrl/Y
     $ ON ERROR THEN GOTO END_WRITE               ! Close the file if an
     $ !                                          !   error occurs
     $WRITE:                                      ! Begin the loop
     $ INQUIRE STUFF "Input data"                 ! Prompt for input
     $ IF STUFF .EQS. "" THEN GOTO END_WRITE      ! Test for the end of
     $ !                                          !   the file
     $ WRITE IN_FILE STUFF                        ! Write to the file
     $ GOTO WRITE                                 ! Go to the beginning
     $END_WRITE:                                  ! End the loop
     $ !                                          !
     $ CLOSE IN_FILE                              ! Close the file

16.10.1 固有の名前を持つファイルの作成

固有の名前を持つファイルを作成するには,F$SEARCHレキシカル関数を使用して, その名前がすでにディレクトリに存在するかどうかを調べます。F$SEARCH についての詳細は,『OpenVMS DCL Dictionary』のレキシカル関数の説明を参照してください。

次のコマンド・プロシージャは,ファイル名を求めるプロンプトを出してから,F$SEARCH レキシカル関数を使用して,省略時のディレクトリで指定された名前を検索します。 指定された名前を持つファイルがすでに存在する場合には,ERROR_1 に制御が渡され,プロシージャは,"The file already exists"のメッセージを表示してから,GET_NAMEラベルに制御を戻します。 このとき,別のファイル名を求めるプロンプトを出します。

     $ ! FILES.COM
     $ !
     $GET_NAME:
     $ INQUIRE FILE "File"           ! Prompt the user for a file name
     $ IF F$SEARCH (FILE) .NES. ""   ! Make sure the file name is unique
     $ THEN
     $    WRITE SYS$OUTPUT "The file already exists"
     $    GOTO GET_NAME
     $ ELSE
     $    OPEN/WRITE IN_FILE 'FILE'  ! Open the file with WRITE access
     $ ENDIF
        .
        .
        .
     $ EXIT

16.11 WRITEコマンドの使用

これ以降の節では,WRITEコマンドの使用について説明します。

16.11.1 データの指定

WRITEコマンドのデータを指定する場合は,第14 章に説明する文字列式の規則に従います。次に,いくつかのデータの指定方法を示します。

     $! Define symbols
     $!
     $ CREATED = "File created April 15, 1996"
     $ COUNT = 4
     $ P4 = "fourth parameter"
     $!
     $! Open the file DATA.OUT for writing
     $!
     $ OPEN/WRITE OUTFILE DISK4:[MURPHY]DATA.OUT
     $!
     $ WRITE OUTFILE CREATED                 【1】
     $ WRITE OUTFILE "CREATED"               【2】
     $!
     $ WRITE OUTFILE "Count is ''COUNT'."    【3】
     $ WRITE OUTFILE P'COUNT'                【4】
     $!
     $ WRITE OUTFILE "Mode is ''f$mode()'"   【5】
     $!
     $ CLOSE OUTFILE

     $ TYPE DISK4:[MURPHY]DATA.OUT <Return>   【6】
     File created April 15, 1996
     CREATED
     Count is 4.
     fourth parameter
     Mode is INTERACTIVE
     $

例を確認する際には,次のことに注意してください。

【1】
書き込むデータを文字列式として指定する。
【2】
CREATED文字列をリテラル文字列として出力ファイルに書き込む。
【3】
リテラル文字列とシンボル名を組み合わせる。
【4】
WRITEコマンドの中で一重引用符を使用してシンボル置換を強制する。 この例では,WRITEコマンドは,値をCOUNTシンボルに置換し, その結果のコマンド文字列(P4)でシンボル置換を行う。
【5】
一重引用符を使用してリテラル文字列とレキシカル関数を組み合わせる。
【6】
前のWRITEコマンドによって出力ファイルDATA.OUT に書き込まれたデータを表示する。

16.11.2 /SYMBOL修飾子の使用

WRITEコマンドは,レコードを書き込むとき,書き込まれたレコードの後にレコード・ ポインタを置きます。WRITEコマンドは,長さが最大2,048バイトまでのレコードを書き込めます。

次のいずれかの条件が存在するときには,/SYMBOL修飾子を使用してレコードを書き込みます。

長いレコードの書き込みについての詳細は,『OpenVMS DCL Dictionary』のWRITEコマンドの説明を参照してください。

16.11.3 /UPDATE修飾子の使用

WRITEコマンドと一緒に/UPDATE修飾子を使用すれば,新しいレコードを挿入せずに, レコードを変更できます。/UPDATE修飾子を使用するには,読み込みと書き込み両用でファイルをオープンしなければなりません。

16.12 READコマンドの使用

READコマンドは,レコードを読み込み,その内容にシンボルを割り当てます。READ コマンドを使用すると,1,024文字以内の長さのレコードを読み込むことができます。 ファイルからデータを読み込むには,次のようにします。

手順 操作
1 読み込み用のファイルをオープンする。
2 読み込みループの先頭にラベルを付ける。

単独のレコードを読み込んだり書き込んだりする場合を除いて,ファイル入出力は常にループの中で行われる。

3 ファイルからデータを読み込む。

READコマンドと一緒に/EDN_OF_FILE修飾子を使用して,レコードを読み込み, その内容をシンボルに割り当てる。/END_OF_FILE修飾子を使用すると, ファイル終わりへの到達時点で,/END_OF_FILE修飾子によって指定されたラベルに制御が渡される。 普通は,読み込みループの終わりを示すラベルを指定する。

4 データを処理する。

ファイルを順に読み込む場合には,現在のレコードを処理してから次のレコードを読み込む。

5 ループの始めに戻る。

ファイル終わりに到達するまで,ループが繰り返される。

6 ループを終了して,ファイルをクローズする。

次のコマンド・プロシージャは,STATS.DATファイルの中のそれぞれのレコードを読み込んで処理します。 ファイルの終端状態が戻るまで,READコマンドを繰り返し実行します。 ファイルの終端状態が戻ると,END_READのラベルが付いた行に分岐します。

     $ OPEN/READ INFILE DISK4:[MURPHY]STATS.DAT  !Open the file
     $ !
     $READ_DATA:                                 !Begin the loop
     $ READ/END_OF_FILE=END_READ INFILE RECORD   !Read a record; test for
     $                                           !  end of file
     $                                           ! Process the data
        .
        .
        .
     $ GOTO READ_DATA                            !Go to the beginning
     $                                           !   of the loop
     $END_READ:                                  !End of loop
     $ CLOSE INFILE                              !Close the file
     $ EXIT

READコマンドのシンボル名を指定すると,コマンド・インタプリタは,現在のコマンド・ レベルのローカル・シンボル・テーブルにシンボル名を置きます。2 つ以上のREADコマンドに同じシンボル名を使用すると,それぞれのREAD コマンドがシンボル名の値を再定義します。たとえば,上例のプロシージャでは,READ コマンドは,ループを通るたびに入力ファイル(STATS.DAT) から新しいレコードを読み込み,このレコードを使用してRECORD シンボルの値を再定義します。

16.12.1 /END_OF_FILE修飾子の使用

ファイルから読み込みを行う場合,普通は,ファイルの終端に到達するまで1 つ1つのレコードを順に読み込んで処理していきます。READコマンドと一緒に/END_OF_FILE 修飾子を使用すれば,ループが作成され,ファイルからレコードを読み込んで処理し, すべてのレコードを読み込み終わった時点でループを終了することができます。

/END_OF_FILE修飾子に指定するラベルは,GOTOコマンドに指定するラベルと同じ規則に従うことに注意してください(GOTO コマンドについての詳細は, 第15章を参照してください)。

ループの中でREADコマンドを使用するときには,必ず/END_OF_FILE修飾子を使用するようにします。 そうしないと,OpenVMSレコード管理サービス(OpenVMS RMS) によってファイルの終端を示すエラー状態が戻されたときに, コマンド・インタプリタは,現在のONコマンドに指定されるエラー・ アクションを実行します。たとえば,OpenVMS RMSがエラー状態%RMS-E-EOF を戻すと,コマンド・プロシージャが固有のエラー処理方法を設定している場合を除いて, コマンド・プロシージャは終了します。

16.12.2 /INDEXと/KEY修飾子の使用

索引順編成ファイルからレコードをランダムに読み込むには,READコマンド修飾子の/INDEX と/KEYを使用します。/INDEXと/KEY修飾子は,索引の中で指定されたキーを探してそのキーに関連するレコードを戻すことによって, レコードをファイルから読み込むように指定します。索引を指定しないと, 主インデックス(0)が使用されます。

レコードをランダムに読み込む場合,/KEYまたは/INDEX修飾子を指定せずにREAD コマンドを使用すると,ファイルの残りの部分を順に読み込むことができます。

16.12.3 /DELETE修飾子の使用

READコマンドと一緒に/DELETE修飾子を使用すると,索引順編成ファイルからレコードを削除できます。/DELETE 修飾子を指定した場合は,レコードの読み込みが終わってからレコードがファイルから削除されます。/DELETE 修飾子と一緒に/INDEXと/KEY修飾子を使用すれば,特定のキーによって指定されたレコードを削除できます。

/DELETE,/INDEX,/KEY修飾子についての詳細は,『OpenVMS DCL Dictionary』のREAD コマンドの説明を参照してください。

16.13 CLOSEコマンドの使用

CLOSEコマンドは,ファイルを閉じ,OPENコマンドで作成された論理名の割り当てを解除します。 コマンド・プロシージャを終了する場合には,その前にコマンド・ プロシージャで開いたすべてのファイルを閉じてください。 開いたファイルを閉じなかった場合には,コマンド・プロシージャが終了するときに, ファイルは開かれたままになり,開かれているファイルに割り当てられている論理名は, プロセス論理名テーブルから削除されません。

次の例では,CLOSEコマンドはファイルSTATS.DATを閉じ,論理名INFILEの割り当てを解除します。

     $ OPEN INFILE DISK4:[MURPHY]STATS.DAT
        .
        .
        .
     $ CLOSE INFILE

16.14 ファイルの変更

この節では,次の3種類のファイル変更方法について説明します。

16.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

レコードはターミナルに表示され,レコードを変更する必要があるかどうかが尋ねられます。 レコードの変更を選択すると,ターミナルから新しいレコードが読み込まれ, 新しいレコードの長さと元のレコードの長さとが比較されます。 元のレコードの方が長い場合には,新しいレコードにスペースを追加して同じ長さにします。 元のレコードの方が短い場合には,エラー・ メッセージが表示され,新しいレコードを求めるプロンプトがもう一度出されます。

16.14.2 新しい出力ファイルの作成

ファイルに大きな変更を加えるには,そのファイルを読み込みアクセス用にオープンし, 新しいファイルを書き込みアクセス用にオープンします。 新しい出力ファイルを作成するので,レコードのサイズを変更したり,レコードを追加したり削除したりできます。

OPEN/WRITEコマンドは,書き込みアクセス用の新しいファイルをオープンします。 新しいファイルは元のファイルと同じ名前にすることができ,バージョン番号は, 古いファイルのバージョン番号より1だけ大きくなります。


注意
読み込み用に正しいファイルがオープンされるように, 既存のファイルを読み込みアクセス用にオープンしてから, 新しいバージョンを書き込みアクセス用にオープンするようにしなければなりません。

ファイルに変更を加えるには,次の手順に従ってください。

手順 操作
1 該当ファイルを読み込みアクセス用にオープンする。

これは入力ファイルであり,このファイルを変更する。

2 書き込みアクセス用に新しいファイルをオープンする。

これは出力ファイルであり,このファイルを作成する。出力ファイルに入力ファイルと同じ名前を指定すると, 出力ファイルは,入力ファイルより1 だけ大きいバージョン番号を持つ。

3 READコマンドを使用して,変更中のファイルからレコードを1 つずつ読み込む。

元のファイルからそれぞれのレコードを読み込むときに,レコードをどのように処理するかを決定する。

4 すべてのレコードが終了するまで,レコードの読み込みと処理を続ける。
5 CLOSEコマンドを使用して,入力ファイルと出力ファイルの両方をクローズする。

次の表では,RECORDシンボルに元のファイルから読み込んだレコードが収められます。

場合 結果
レコードに変更なし 新しいファイルに同じシンボルを書き込む。
レコードに変更あり INQUIREコマンドを使用して別のレコードをシンボルに読み込んでから, 変更済みのシンボルを新しいファイルに書き込む。
レコードが削除されている シンボルを新しいファイルに書き込まない。
レコードが挿入されている ループを使用して,シンボルにレコードを読み込んだり, シンボルを新しいファイルに書き込んだりする。

例:出力ファイルの作成

次の例は,入力ファイルからレコードを読み込み,レコードを処理してから出力ファイルにコピーするコマンド・ プロシージャを示しています。

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

16.14.3 ファイルへのレコードの追加

OPEN/APPENDコマンドは,既存のファイルの終端にレコードを追加します。 レコードをファイルに追加するには,次のようにします。

手順 操作
1 OPENコマンドと一緒に/APPEND修飾子を使用して, レコード・ポインタをファイルの終端に置く。

/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

16.15 ファイル入出力エラーの処理方法

OPEN,READ,WRITEコマンドのうちのいずれかと一緒に/ERROR修飾子を使用すると, システム・エラー・メッセージを表示せずに指定されたラベルに制御を渡すことができます。 入出力操作中にエラーが生じた場合には,/ERROR 修飾子は他のすべてのエラー制御方式(READコマンドの/EDN_ 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$STATUSレキシカルの内容を表示するためにレキシカル関数 F$MESSAGE が使用されています。

     $ OPEN/READ/ERROR=CHECK FILE 'P1'
        .
        .
        .
     $ CHECK:
     $  ERR_MESSAGE = F$MESSAGE($STATUS)
     $  WRITE SYS$OUTPUT "Error opening file: ",P1
     $  WRITE SYS$OUTPUT ERR_MESSAGE
        .
        .
        .

16.15.1 省略時のエラー・アクション

OPEN,READ,WRITE,またはCLOSEコマンドを使用しているときにエラーが生じ, エラー・アクションを指定しないと,現在のONコマンド・アクションが有効になります。

READコマンドがファイルの終端メッセージを受け取った場合のエラー・アクションは, 次のようにして決定されます。

16.16 実行フローの制御方法

コマンド・プロシージャでの通常の実行フローは順次です。つまり,ファイルの最後に到達するまで, プロシージャ内のコマンドが順に実行されます。 しかし,特定の文を実行するかどうかなど,プロシージャの実行を続行するときの条件を制御することもできます。

この後の節では,次のことについて説明します。

16.16.1 IFコマンドの使用

IFコマンドは,式の値をテストして,式の結果が真の場合にコマンドまたはコマンドのブロックを実行します。 式の結果が偽の場合には,次のいずれかになります。

DCLのIFコマンドには,2つの形式があります。1つは,第15章で説明されているように IF コマンドに指定された式が真の場合に単独のコマンドを実行する形式です。

DCLにはブロック構造のIF形式もあります。ブロック構造のIFコマンドは, 指定された式が真の場合に複数のコマンドを実行します。ELSE文を指定すれば, 式が偽の場合に1つ以上のコマンドを実行することができます。

16.16.2 THENコマンドの使用

式が真の場合に複数のコマンドを実行するには,動詞(前にドル記号が付いたDCL コマンド)としてTHEN文を指定し,残りのブロック構造文の終わりにENDIF 文を指定します。

次の例では,THEN文は動詞として使用されています。

     $ IF expression
     $   THEN
     $       command
     $       command
        .
        .
        .
     $ ENDIF

16.16.3 ELSEコマンドの使用

式が偽の場合に1つ以上のコマンドを実行するには,動詞としてELSE文を指定し, 残りのブロック構造文の終わりにENDIF文を指定します。

次の例では,ELSEコマンドは動詞として使用されています。

     $ IF expression
     $   THEN
     $       command
     $       command
        .
        .
        .
     $   ELSE
     $       command
     $       command
        .
        .
        .
     $ ENDIF

16.16.4 コマンド・ブロックの使用

同じコマンド・プロシージャでコマンドを実行するのか,別のコマンド・ プロシージャにコマンドを置いて実行するのかに応じて,コマンド・ブロックにはいくつかの実行方法があります。 ガイドラインは次に示します。

IF式の結果が真の場合には,THENコマンドの後のコマンド・ブロックを実行できます。 コマンド・ブロックを使用する場合には,IFコマンドの後の行に最初のコマンドとしてTHEN コマンドを指定します。

次の例では,2つのSET TERMINALコマンドが実行され,F$MODE が"INTERACTIVE"に等しい場合には,プロシージャは制御をラベルPROCEED に渡します。F$MODEが"INTERACTIVE"に等しくない場合には,プロシージャは終了します。

     $ IF F$MODE () .EQS. "INTERACTIVE"
     $ THEN
     $    SET TERMINAL/DEVICE=VT200
     $    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コマンドを実行します。条件が真でない場合には, プロシージャは2つのメッセージをSYS$OUTPUTに書き込みます。

IF-THEN-ELSEを使用する場合には,次のような制限事項があります。

真の文

IFコマンドに続く文は,1つ以上の数値定数,文字列リテラル,シンボリック名, レキシカル関数で構成し,論理演算子,算術演算子,または文字列演算子によって区切ります。 次のいずれかの値を持つ場合に式は真となります。

偽の文

次のいずれかの値を持つ場合に式は偽となります。

式の作成

IFコマンドの式を作成する場合には,特に,次の点に注意してください。

この後に示す例は,IFコマンドと一緒に使用できる式を示しています。 さらに詳しい例が必要であれば,『OpenVMS DCL Dictionary』のIFコマンドの説明を参照してください。

次の例では,論理演算子を使用し,THEN文の後の1つのコマンドだけを実行します。 シンボル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コマンドの後のコマンドを実行します。

次の例は,2つの異なるIFコマンドを示しています。

     $ COUNT = 0
     $ LOOP:
     $    COUNT = COUNT + 1
     $    IF COUNT .EQ. 9 THEN EXIT
     $    IF P'COUNT' .EQS. "" THEN EXIT
        .
        .
        .
     $ GOTO LOOP

最初のIFコマンドは2つの整数を比較し,2番目のIFコマンドは2つの文字列を比較しています。 整数比較には.EQ.演算子が,文字列比較には.EQS. 演算子が使用されています。

最初に,COUNTの値が整数の9と比較されます。値が等しいとプロシージャは終了し, 値が等しくないとプロシージャは継続されます。8つのパラメータ( 許容最大数)を処理した後,ループが終了します。

2番目の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

16.16.5 GOTOコマンドの使用

GOTOコマンドは,コマンド・プロシージャ内のラベルの付いた行に制御を渡します( ラベルの使い方についての詳細は,第15 章を参照してください)。GOTOコマンドは,THEN句で使用すると特に便利であり, プロシージャを前後に分岐させることができます。たとえば, コマンド・プロシージャでパラメータを使用する場合には,プロシージャの先頭でパラメータをテストし, 適切なラベルに分岐できます。

GOTOコマンドまたはGOSUBコマンドの分岐先ラベルは,独立したIF-THEN- ELSE構造または独立したサブルーチンの内部に存在できません。

次の例では,IFコマンドは,P1が空文字列でないことをチェックします。

     $ IF P1 .NES. "" THEN GOTO OKAY
     $ INQUIRE P1 "Enter file spec"
     $ OKAY:
     $ PRINT/COPIES=10 'P1'
        .
        .
        .

P1が空文字列の場合には,GOTOコマンドは実行されず,INQUIREコマンドはパラメータ値を要求するプロンプトを表示します。 それ以外の場合には,GOTO コマンドはINQUIREコマンドの後に分岐します。どちらの場合も, プロシージャは,OKAYというラベルの付いた行の後の PRINTコマンドを実行します。

次の例では,GOTOコマンドはエラー・メッセージを戻します。これは,分岐先(TEST_1) がIF-THEN構造の内部に存在するからです。

     $ 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

16.16.5.1 再実行の回避

正常に実行されたジョブの一部が再実行されないようにするときにもGOTO コマンドを使用できます。この場合は,次のようにします。

手順 操作
1 プロシージャの中の開始ポイントにラベルを付ける。
2 ラベルの後, SET RESTART_VALUE =ラベル名 コマンドを使用して,再開ポイントをそのラベルに設定する。

SET RESTART_VALUE =ラベル名 コマンドの実行後にバッチ・ ジョブが中断された場合,システムはバッチ・ジョブを再開するときにグローバル・ シンボルBATCH$RESTARTに該当するラベル名を割り当てる。

3 プロシージャの先頭で,$RESTART シンボルの値をテストする。

$RESTARTが真の場合には,BATCH$RESTARTシンボルを転送ラベルとして使用してGOTO 文を実行する。

$RESTARTグローバル・シンボル

$RESTARTは,システムがユーザ用に残してある予備グローバル・シンボルです。 いずれか1つのバッチ・ジョブが中断後に再開された場合は,$RESTART は真であり,そうでない場合は,$RESTARTは偽です。予備グローバル・ シンボル$RESTARTは削除できません。

コマンド・プロシージャの中にSET RESTART_VALUEコマンドがあり,この要素の中でジョブを再実行したい場合には,SET ENTRY/NOCHECKPOINT コマンドを入力して, グローバル・シンボルBATCH$RESTARTを削除します。中断されたジョブを再起動した場合には, ジョブは中断されたセクション内で実行されます。

次のコマンド・プロシージャは,バッチ・ジョブの中で値を再開する方法を示しています。

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

このコマンド・プロシージャを再開可能なバッチ・ジョブとしてキューに登録するには, ジョブを登録するときに SUBMITコマンドと一緒に/RESTART 修飾子を指定します。割り込まれたジョブを再開する場合は, 割り込みのあった場所からジョブの実行が開始されます。たとえば, SORT_FILEルーチンの中でジョブに割り込みがあった場合は,ジョブを再開すると,SORT_FILE ラベルから実行が開始されます。

システムで障害が発生した場合には,プロセス環境の大部分は維持管理されません。 システム障害が発生した後も管理されるシンボルは,$RESTART とBATCH$RESTARTだけです。したがって,コマンド・プロシージャで使用したシンボルやプロセス論理名は, それぞれのSET RESTART_VALUEコマンドの後で再定義するか, または$RESTARTが真のときに実行されるTHENブロックの内部で再定義しなければなりません。 シンボルと論理名をTHENブロックの内部で定義した場合には, コマンドGOTO 'BATCH$RESTART'をTHENブロックの最後のコマンドとして指定しなければなりません。

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レキシカル関数の使用方法についての詳細は,『OpenVMS DCL Dictionary 』を参照してください。

次の例では,ファイル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コマンドについての詳細は,『OpenVMS DCL Dictionary: N-Z』を参照してください。

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

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

16.20.1 条件コマンド実行のためのPIPEコマンドの使用

先行するコマンド・シーケンスの実行結果により,次のコマンド・シーケンスが実行されます。

PIPE command-sequence1 && command-sequence2

command-sequence1が成功した時のみ,command- sequence2が実行される点に注意してください。次の形式を使用すると, command-sequence1が失敗した時のみcommand- sequence2が実行されます。

PIPE command-sequence1 || command-sequence2

16.20.2 パイプライン実行のためのPIPEコマンドの使用

パイプラインは,縦線(|)区切り文字で表されるパイプで接続されたパイプライン・ セグメント・コマンドのシーケンスです。パイプライン・ セグメント・コマンドとは,パイプライン内のDCLコマンドです。 パイプはパイプライン・セグメント・コマンドのSYS$OUTPUTを,次のコマンドのSYS$INPUT に接続します。パイプラインの形式を次に示します。

PIPE pipeline-segment-command | pipeline-segment-command [| . . . ]

各パイプライン・セグメント・コマンドは,SYS$OUTPUTを次のパイプライン・ セグメント・コマンドのSYS$INPUTに接続し,別々のサブプロセスで実行されます。 これらのサブプロセスはパラレルに実行されます。ただし, 最初のパイプライン・セグメント・コマンドを除く各パイプライン・ セグメント・コマンドが,その先行パイプライン・セグメント・コマンドの標準出力をその標準入力として読み込む程度に同期化されます。 最後のパイプライン・ セグメント・コマンドが終了すると,パイプラインは実行を終了します。

通常,パイプラインでは"フィルタ・アプリケーション"を使用します。フィルタ・ アプリケーションとは,SYS$INPUTからのデータを取り,指定した方法でデータを変換し, それをSYS$OUTPUTに書き込むプログラムのことです。

パイプラインのコンテキストでは,DCLの機能は一部異なります。次に例を示します。

16.20.3 サブシェル実行のためのPIPEコマンドの使用

サブシェルは,区切り文字で区切られ,括弧で囲まれた1つまたは複数のコマンド・ シーケンスです。サブシェルの形式を次に示します。

PIPE ( command-sequence [separator command-sequence]... )

サブシェル内のコマンド・シーケンスは,サブプロセス環境で実行されます。 DCL は,サブシェルが終了してから,次のコマンド・シーケンスを実行します。( ) 区切り文字は,SPAWN/WAITコマンドに似ています。

16.20.4 バックグラウンド実行のためのPIPEコマンドの使用

コマンド・シーケンスは,次の形式を使用してサブプロセス環境で実行されます。

PIPE command-sequence [ separator command-sequence]... &

DCLは,コマンド・シーケンスの終了を待ちません。バックグラウンド・ サブプロセスが作成されると,制御はDCLに戻ります。

16.20.5 入出力リダイレクトのためのPIPEコマンドの使用

コマンド・シーケンスは,次のようにコマンドの実行中に, SYS$INPUT, SYS$OUTPUT,またはSYS$ERRORをファイルにリダイレクトできます。

パイプライン・セグメント・コマンドも, SYS$INPUT,SYS$OUTPUT,またはSYS$ERROR をリダイレクトできます。ただし,SYS$OUTPUTのリダイレクトは, 最後のパイプライン・セグメント・コマンドだけに使用でき, SYS$INPUTのリダイレクトは,最初のパイプライン・セグメント・コマンドだけに使用できます。

PIPEコマンドのリダイレクトは,DEFINEコマンドやASSIGNコマンドを使用して行うリダイレクトとは 異なります。相違点を以下に示します。

SYS$OUTPUTをリダイレクトすると,コマンド・シーケンスが実際にSYS$OUTPUT に書き込むかどうかにかかわらず,常にリダイレクトされた出力ファイルが作成されます。 リダイレクトされた出力ファイルと同じ名前を持つファイルのバージョンがすでに存在する場合, そのファイルの新しいバージョンが作成されます。 この動作は,スーパバイザ・モードでDEFINE またはASSIGNコマンドを使用して, SYS$OUTPUTを再定義する場合と同じです。 リダイレクトされたファイルは,コマンド・シーケンスが実行される前に作成されることに注意してください。 次の例のように,リダイレクトされたファイルがコマンド・ シーケンスでも使用される場合は, 操作が失敗することがあります。

     $ PIPE SEARCH TRANS.LOG "alpha" > TRANS.LOG

     %SEARCH-W-OPENIN, error opening TRANS.LOG;2 as input
     -RMS-E-FLK, file currently locked by another user

この例では,新しいバージョンのTRANS.LOGが作成され,書き込みアクセス用にオープンされます。 次にSEARCHコマンドが,前のバージョンではなく最新バージョンの TRANS.LOG に読み込みアクセスしようとします。

SYS$ERRORをリダイレクトすると,コマンド・シーケンスが実行中に実際にSYS$ERROR に書き込む場合のみ, リダイレクトされたファイルが作成されます。 リダイレクトされたエラー・ファイルと同じ名前を持つ既存のファイルは存在しません。 リダイレクトされたエラー・ファイルと同じ名前を持つファイルがすでに存在する場合は, そのファイルがリダイレクトされたエラー・ ファイルとしてオープンされます。次に,このコマンド・シーケンスで作成されたエラー出力は, リダイレクトされたエラー・ファイルの最後に追加されます。 この動作は,スーパバイザ・モードでDEFINEまたはASSIGN コマンドを使用して, SYS$ERRORを再定義する場合と同じです。

16.20.6 PIPEコマンドへの割り込み

Ctrl/Yを押すと,PIPEコマンドに割り込みをかけることができます。 PIPEコマンドをパイプラインまたはサブシェル・コマンド・シーケンスで実行している場合は, コマンド・シーケンスとPIPEコマンドが削除されます。 この場合,割り込み直後にCONTINUEコマンドを入力しても, PIPEコマンドの実行は再開されません。

PIPEコマンドが,サブシェル・コマンド・シーケンスまたはパイプライン・ コマンド・シーケンス以外の,コマンド・シーケンスを実行している場合は,DCL は,コマンド・シーケンスがPIPEコマンド動詞なしで入力され, Ctrl/Y で割り込みをかけられたように動作します。 Ctrl/Y 割り込みについての詳細は,第3.11.2項および第15.11節を参照してください。

16.20.7 サブプロセスの性能向上

PIPEコマンドは,実行中に多数のサブプロセスを作成できます。通常, コマンド・シーケンスにより起動されるアプリケーションは,プロセスの論理名とシンボル名に依存しません。 この場合,/NOLOGICAL_NAMEおよび/NOSYMBOLS 修飾子を使用すると,サブプロセスを素早く作成できます。 これらの修飾子により,プロセスの論理名とシンボル名は,PIPEコマンドにより作成されたサブプロセスに渡されなくなります。

PIPEコマンドの使用例を次に示します。


[ 前のページ ] [ 次のページ ] [ 目次 ] [ 索引 ] [ DOC Home ]