For Development HEAD DRAFTSearch (procedure/syntax/module):

3.3 Schemeスクリプトを書く

goshのコマンドラインにSchemeプログラムのファイル名が渡された場合、 goshuserモジュールをカレントモジュールとし、 それ以降のコマンドライン引数のリストをグローバル変数*argv*に束縛して、 Schemeプログラムをロードします。もしscheme-fileの最初の行が“#!”で始まって いたら、その行は無視されます。これにより、Unix系のシステムで実行可能なSchemeスクリプト を書くことが出来ます。

典型的なGaucheスクリプトの最初の行は次のようなものです。

#!/usr/local/bin/gosh
  または,
#!/usr/bin/env gosh
  または,
#!/bin/sh
#|
exec gosh -- $0 "$@"
|#

後の2つは「シェルトランポリン」テクニックを用いて、goshがPATHにあるディレクトリの どこかにあれば起動できるようにしています。3番目の方法は、 goshにいくつかコマンドラインオプションを渡したい時に便利です。

ファイルが正常にロードされたら、goshは userモジュールに ‘main’ という手続きが定義されているかどうか調べ、 定義されていればそれを呼びます。mainには、スクリプトへの引数のリストが 唯一の引数として渡されます。リストの最初の要素はスクリプトファイル名です。

mainが整数の値を返したら、goshはその値を終了ステータスとして終了します。 mainが整数以外の値を返した場合はgoshは終了ステータス70 (EX_SOFTWARE)で終了します。このふるまいはSRFI-22と互換です。

mainが定義されていなければgoshはロード後にそのままステータス0で 終了します。

シェルスクリプトやPerlスクリプトと同じように、スクリプトのボディに直接 実行される式を書くこともできますが、なるべく ‘main’ を使った方法を 使うことをお薦めします。そうすると、スクリプトをインタプリタにインタラクティブに ロードしてデバッグすることもできます。

-mコマンドラインオプションを使えば、userモジュール以外の モジュールで定義されたmain手続きをスクリプトのメイン関数として 呼ぶことができます。Schemeモジュールを、Schemeスクリプトとしても使えるように したい場合に便利です。

例えば、fooというSchemeモジュールを書いて、その中で main関数を定義しておきます。このmain関数はexportしないでおきます。 このファイルがモジュールとしてロードされた場合、このmain関数は 外からは見えないので何もしません。しかし、gosh-m fooオプションを 与えて、このファイルをスクリプトファイルとして指定すれば、ファイルをロードした後に main手続きが呼ばれます。その中には、 テストだとかモジュールのサンプルアプリケーションを書いておくことができるでしょう。

R7RSスクリプトに関する註: スクリプトがR7RS Schemeで書かれている場合 (先頭にR7RSのimportがあることで区別されます。 詳しくは3つのimport形式参照)、 スクリプト本体はr7rs.userへと読み込まれるため、 mainは自動的には呼ばれません。 コマンドライン引数-mr7rs.mainを指定することで、 R7RSスクリプトのmainを実行できます。 別の方法として、SRFI-22に指定されているように、 スクリプトインタプリタのbasenameがscheme-r7rsであった場合、 スクリプトはR7RSで書かれたSRFI-22形式であると見なされ、 userモジュールのかわりにr7rs.userモジュールのmainが呼ばれます。 そのような別名は自動的にはインストールされませんが、 goshscheme-r7rsという名前でシンボリックリンクを張るか、 コピーすることができるでしょう。

コマンドライン引数を受け取る標準的な方法は、main関数の引数としてですが、 他にもコマンドライン引数にアクセスする方法が提供されています。詳しくは コマンドライン引数を参照してください。

ではいくつか簡単な例を示しましょう。最初の例はUnixのcat(1)コマンドを模するものです。 エラー処理やコマンドラインオプションの処理は行っていません。

#!/usr/bin/env gosh

(define (main args)   ;entry point
  (if (null? (cdr args))
      (copy-port (current-input-port) (current-output-port))
      (for-each (lambda (file)
                  (call-with-input-file file
                    (lambda (in)
                      (copy-port in (current-output-port)))))
                (cdr args)))
  0)

次のスクリプトは簡単なgrepコマンドです。

#!/usr/bin/env gosh

(define (usage program-name)
  (format (current-error-port)
          "Usage: ~a regexp file ...\n" program-name)
  (exit 2))

(define (grep rx port)
  (with-input-from-port port
    (lambda ()
      (port-for-each
       (lambda (line)
         (when (rxmatch rx line)
           (format #t "~a:~a: ~a\n"
                   (port-name port)
                   (- (port-current-line port) 1)
                   line)))
       read-line))))

(define (main args)
  (if (null? (cdr args))
      (usage (car args))
      (let ((rx (string->regexp (cadr args))))
        (if (null? (cddr args))
            (grep rx (current-input-port))
            (for-each (lambda (f)
                        (call-with-input-file f
                          (lambda (p) (grep rx p))))
                      (cddr args)))))
  0)

また、gauche.parseopt - コマンドライン引数の解析を使うと手軽にコマンドラインオプション を処理することができます。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT