表題

Unix における Scheme スクリプトの実行

著者

Martin Gasbichler、Michael Sperber

状態

この SRFI は現在「確定」の状態である。 SRFI の各状態の説明については ここ を参照せよ。 この SRFI に関する議論については メーリングリストのアーカイブ を参照せよ。

概要

この SRFI では、Scheme プログラムを一貫した方法で Unix スクリプトとして実行するために必要な基本事項について述べる。 特に、次の事柄について述べる。

論拠

たとえプラットフォームが Unix であると分かっていても、R5RS に準拠し、単一のファイルに書かれた Scheme プログラムであっても、それを実行するための標準的な方法はない。ほとんどすべての Scheme 処理系は、Scheme システムを起動して特定のファイルをロードし実行する方法を提供しているが、その方法は Scheme 処理系ごとに異なっている。

このように事実上の標準がないため、エンドユーザー向けの単純なプログラムであっても、特定の Scheme 処理系を配布するか、処理系に固有の多くのトリックを使わなければ、そのプログラムを実行することができない状況にある。そこでこの SRFI では、Scheme で書かれたポータブルな Unix スクリプトを作成するための一連の慣習について述べる。

残念なことに、Scheme 処理系に固有の慣習が存在するため、既存の方法と互換性を保ちながらここで述べる慣習を公式化することは不可能であろう。 「設計の論拠」のセクションでは、このことに関する大まかな概論を述べる。

仕様

スクリプトの構文

<script> --> <script prelude>? <program>
<script prelude> --> #! <space> <any character that isn't a line break>* <line break>

<script prelude> の行は 64 文字より長くあってはならない。 (この論拠についてはここを参照せよ。)

スクリプト インタプリタの起動

この SRFI をサポートするシステムは、サポートする言語方言に応じて Scheme スクリプト インタプリタと呼ばれるバイナリ実行ファイルを選択する機能を提供する。 このシステムでは、通常のパス上に scheme-rnrsscheme-ieee-n-yscheme-srfi-0scheme-srfi-7 のいずれかのプログラムを提供する。これらのインタプリタを起動するため構文は、いずれも次のようにする。

<executable> <file> <argument> ...

Scheme スクリプト インタプリタは、標準的な Unix のパス上に存在することが推奨される。さらに、スクリプトから Scheme スクリプト インタプリタを起動する方法として、次のような /usr/bin/env を介したトランポリン実行が推奨される。

#! /usr/bin/env <executable>

意味論

Scheme スクリプト インタプリタは <file> で指定されたファイルをロードする。インタプリタは script prelude を無視し、ファイルの残りの部分をインタプリタの名前で示される言語方言に従って解釈実行する。

Scheme スクリプト インタプリタは、<file> をロードすることと意味論的に等価であるという事実を妥当な方法で確認した上で、別のファイルをロードすることも許される。たとえば、スクリプト インタプリタは、関連する名前をもつファイル (たとえば追加の拡張子をもつファイル) が <file> のコンパイル済みバージョンであると仮定することができる。

scheme-rnrs は、コードが RnRS Scheme で書かれていることを要求する。scheme-ieee-n-y は、コードが IEEE n-y Scheme で書かれていることを要求する。特に、scheme-r4rs はコードが R4RS Scheme で書かれていることを要求し、scheme-r5rs はコードが R5RS Scheme で書かれていることを要求し、scheme-ieee-1178-1990 はコードが IEEE 1178-1990 Scheme で書かれていることを要求する。scheme-srfi-0 は、コードが R5RS Scheme と SRFI 0 の拡張機能を使用して書かれていることを要求する。scheme-srfi-7 は、コードが R5RS Scheme と SRFI 7 の拡張機能を使用して書かれていることを要求する。

スクリプトを起動すると、Scheme システムは main という名前の手続きに引数を 1 つ指定して呼び出す。この引数はスクリプトへの Unix コマンドライン引数を含む文字列リストである。つまりこのリストは、Scheme スクリプト インタプリタのプロセスへの argv 配列のインデックス 1 から最後までの要素からなる。

main 手続きは整数を返さねばならず、これがスクリプトの終了ステータスとなる。

スクリプトの実行中に (R5RS, Section 1.3.2 の意味で) エラーが起きるとスクリプトはすぐに終了し、そのときの終了ステータスは、C 言語の sysexits.h で定義されているマクロ EX_SOFTWARE であるか、sysexits.h が存在しなければ 70 となる。

main 手続きが整数以外の戻り値をもつ場合も、スクリプトは EX_SOFTWARE を返す。

上記のようなエラーが起きた場合、インタプリタは stderr に意味の分かるエラー メッセージを表示することが推奨される。

スクリプト インタプリタが (いまだ書かれていない将来の SRFI 機能を使用して) スクリプトに環境へのアクセスを許す場合、スクリプトが取得する環境はスクリプト インタプリタの起動時の環境に同一でなければならない。

この SRFI をサポートする Scheme 処理系は、これらすべてのスクリプト インタプリタを提供する必要はない。しかし、 IEEE 1178-1990 または R5RS を実装しているのであれば scheme-ieee-1178-1990 を提供し、 別の IEEE 標準 Scheme を実装しているのであれば scheme-ieee-n-y を提供し、 RnRS (n>=4) を実装しているのであれば scheme-rnrs を提供し、 SRFI 0 を実装しているのであれば scheme-srfi-0 を提供し、 SRFI 7 を実装しているのであれば scheme-srfi-7 を提供することが推奨される。

scheme-srfi-7 の場合、すべてのファイル名指定 (SRFI 7 の構文で <filename> と記述されているもの) は Unix スタイルのファイル名の文字列リテラルで、絶対ファイル名、または、スクリプトが存在するディレクトリに対する相対ファイル名とする。

スクリプトの対話的なロード

SRFI 22 をサポートする対話的な開発環境をもつ Scheme 処理系は、Scheme スクリプトをその環境にロードする機能をもつことを推奨する。

互換性

スクリプトをネイティブ コード互換にしたいプログラマは、スクリプト先頭の起動行を次のような形式で記述することを推奨する。

#! ... <executable>

<executable> には、スクリプト インタプリタの上記リストのうちの 1 つの名前を指定する。/usr/local/bin/scheme-r5rs のように、頭にディレクトリ名をつけてもよい。

ネイティブ実行形式へのコンパイルをサポートする Scheme システムは、このようなスクリプト先頭の起動行を見て言語方言を決定することが期待される。

以下に Unix の cat ユーティリティの Scheme 版を示す。

#! /usr/bin/env scheme-r5rs

(define (main arguments)
  (for-each display-file (cdr arguments))
  0)

(define (display-file filename)
  (call-with-input-file filename
    (lambda (port)
      (let loop ()
  (let ((thing (read-char port)))
    (if (not (eof-object? thing))
        (begin
    (write-char thing)
    (loop))))))))

設計の論拠

Unix における Scheme 処理系のほとんどが、それぞれ異なる方法で Unix スクリプトをサポートしている。残念ながら、スクリプト自体の構文だけでなく起動の構文までもが実装ごとに異なっている。しかし、この SRFI の設計は注意深く行われた。

スクリプト インタプリタはバイナリであること

この SRFI に従うスクリプト インタプリタは、バイナリ実行ファイルである必要がある。 シェル スクリプトであれば、起動行から直接使用することは不可能である。 ほとんどの Unix では、起動行のスクリプト インタプリタはバイナリであることを要求する。

起動行はオプションである

スクリプトを標準の Scheme ファイルとして書き、明示的に Scheme スクリプト インタプリタを起動することが可能となるように、起動行はオプションとする。 これにより、(他の環境のスクリプトに関する将来の SRFI がこの例に従うとすれば) たとえば異なる環境でもポータブルなメイクファイルを書くことが可能となる。

スクリプトの絶対位置 vs. トランポリン

この SRFI では Scheme スクリプト インタプリタの名前を規定しているが、その絶対位置は規定していない。ほとんどの Unix ではスクリプト内のインタプリタが絶対ファイル名で指定されることを要求するので、インタプリタを起動するためのポータブルな唯一の方法は、例に示した /usr/bin/env (いわゆる トランポリン) のような標準 Unix プログラム を呼び出すことである。

これは、Unix システムではサードパーティ製のソフトウェアの位置が慣習として定着していないことに原因がある。あるシステムまたは環境で慣習となっていることが、別のシステムまたは環境では実現不可能であることもある。さらに、一般的にトランポリンは Unix では非常に安価であり、Scheme スクリプトの解釈実行にかかるコストが、ほとんど常にトランポリンにかかるコストを大きく上回る。

移植性

移植性という用語は、この SRFI の文脈では相対的な用語である。 ほとんどの Unix では #! の慣習をサポートしているにも関わらず、 Posix および The Single Unix Specification はスクリプト自動実行の方法を保証してはいない。 さらに、この 2 つは、/usr/binenv 実行ファイルが存在することも保証してはいない。にも関わらず、多くのシステムにはこの実行ファイルが存在する。議論のアーカイブには多くのシステムの記録が含まれている。

より重要な移植性の問題は、スクリプトの起動行の長さの問題である。 つい最近まで、多くの Unix が起動行に 32 文字の制限をかけていた。 しかし、それらのシステムの最新バージョンでは、64 文字に制限を緩めているか、制限を完全になくしているものがほとんどである。(実際には、我々がテストしたシステムではすべてそうなっていた。) 繰り返すことになるが、議論のアーカイブにはより詳細なデータが記録されている。 Scheme スクリプト インタプリタの名前は分かりやすく長めになっているので、起動行で 32 文字の制限を超える可能性があり、制限を 64 文字までとした。

コマンドライン パラメータへの暗黙的な vs. 明示的なアクセス

この SRFI では、Scheme スクリプト インタプリタはスクリプトへのコマンドライン引数を、main 手続きのリスト引数として渡すことと規定している。 このような引数を保持するグローバル変数を利用する Scheme 処理系もある。 どちらの方法が本質的に好ましいかは明瞭ではない。ベクタとリストのどちらがより自然なデータ構造であるかも明瞭ではない。

しかし、明示的にエントリ ポイントを指定することにより、REPL タイプの Scheme 処理系におけるデバッグが容易になるという利点がある。REPL から明示的に main を呼び出すことが容易であり、この呼び出しはインタプリタがスクリプトをロードするのと同じ効果をもつからである。

コマンドライン パラメータを引数として渡す vs. データ構造として渡す

コマンドライン パラメータを main 手続きに渡すときに、各パラメータを個別の引数として渡すべきか、それとも、1 つのデータ構造体として渡すべきか、という問題に対して、メーリングリストでいくつかの議論がなされた。両者とも文脈に応じて少々の利便性を与える。しかし、各パラメータを個別の引数として渡すことは、意味論的な正確さ ("main 呼び出しで引数の個数が不正であるというエラーが起きた場合、スクリプトの終了コードはどうなるのか?") と実装の容易さという点で問題になった。 (このメールおよびそれに続く議論を参照せよ) 。

コマンドライン引数を順に指定する vs. スイッチで明示的に指定する

この SRFI の直前のドラフトでは、言語方言、エントリ手続き、およびスクリプトのファイル名は Unix スタイルのコマンドライン スイッチで指定することを要求していた。 この要求に従うと、コマンドラインを解析するための追加の機構が必要になる。さらに、スクリプト インタプリタをトランポリン経由ではなく直接起動することが不可能になる。この起動方法ではスクリプト インタプリタに 1 つのコマンドライン引数しか渡せない。 [訳注:この部分は訳が間違っている可能性があります。]

Windows との互換性

Windows のスクリプト構文は Unix のスクリプト構文と根本的に互換性がないように思われる。そのため、Unix と Windows の両方で起動できるスクリプトを 1 つのファイルに書くことは不可能である。 (この議題に関しては Marc Feeley のメール も参照せよ。) Windows で動作する別のスクリプト構文を策定するのがよいであろう。しかし、それはこの SRFI の範囲を超える事柄である。しかし、Windows ではファイル拡張子に実行ファイルを関連付けることができるので、Unix の Scheme スクリプトを Windows システムで実行することが可能であろう。 (この議題に関しては Eli Barzilay のメール を参照せよ。)

実装

実装は処理系に大きく依存する。 また、Unix シェルから呼び出し可能な Scheme 処理系であれば、この SRFI を実現することはそれほど難しくないことは明らかである。 そのため、この SRFI には参照実装を記載しないことにする。

著作権

Copyright (C) Martin Gasbichler and Michael Sperber (2001). All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


編集者:Shriram Krishnamurthi