The F programming language, 1st edition
The FUNC utility command is in patent application of Japan.

 
 
 
 
 
 
 
 
 
 
目 次
  • まえがき
  • §1.はじめに
  • §2.やさしい入門
  • §3.引 数
  • §4.関 数
  • §5.プリプロセッサ
  • §6.変 数
  • §7.制御構造
  • §8.コマンドの作り方
  • あとがき
     
     
     
     
     
     
     
     
     
     
    まえがき
     この度は、本 func 関数ユーティリティ・コマンドをご指定いただきありがとうございます。この func は、作ろうとして出来上がったものではなく、好きな OS-9 というオペレーティング・システムを使っていたら、たまたま知らない間に出来ていたというものです。
     func コマンドは、C言語の重要な要素のひとつである関数という概念をコマンドという形で外に出してみたものです。C言語は関数の集合体からなり、モジュラ・プログラミングがし易い言語です。それがシェル・プログラミングにも採り入れられてC言語と同じ手続き型という言語構造が初めて実現したことになります。
     また、従来のようにシェル自身がすべてを行うのではなく、シェルはコマンド・インタープリタとしてのみ機能してもらっています。その結果、if などの制御構造もすべて外付けコマンドとなっています。ここでは完璧にすべてがコマンドの集合体から構成することを基本としています。
     この内、基本コマンドである func について勝手ながら特許申請させて頂きました。なお、func コマンドが創り出すプログラミング言語をF言語と呼ぶことにしましたが、F言語は制御構造を持たないため文法はありません。そこは皆さんに任せたい(開放します)と思います。極力、小生の影響を少なくする形で使って頂ければと思っています。皆さんの自由な参加と、自由にプログラミングされることをお待ちしています。

    2000年(平成12年) 6月 
    緑に囲まれた木曽の山中にて
    作者  和泉 博 

     
     
     
     
    OS-9
     米国アイオワ州、Des Moines市にある Microware systems社が 1980 年に開発したマルチタスク・マルチユーザのオペレーティング・システムです。世界で唯一の、徹底したモジュール構造により実現されている OS でもあります。当然、カーネルは当初からマイクロ・カーネルとなっています。現在は、そのモジュール構造からコンパクト性の長所を生かして、制御系でシェアを獲得しています。Motorola の 680x0 チップをメインとして、powerPC, Pentium, superSH, MIPS などに対応しています。

     
     
     
     
     
     
     
     
     
     
    §1.はじめに
     ここに配布する func 関数ユーティリティ・コマンドは、シェル・プログラミングのための基本コマンドです。ここでは、func 関数コマンドが創り出すプログラミング言語のことをF言語と呼んでいます。この解説を始めるにあたり、func 関数コマンドが創造するシェル・プログラミング環境は、よく見られる今までのシェル・プログラミングの物真似ではなく、まったく新しい考えによるものであることをお断りしておきます。したがって、本製品はそのアルゴリズムについて特許出願中(PAT.P)であり、現在はパリ条約の優先権を背に米国への併願も検討中のものです。
     簡単にその内容を説明しますと、シェル自身がシェル・プログラムを実行・制御するという現在のやり方を改めて、間接的にこれを実行・制御させるものです。シェルを中間言語として利用する初めての試みです。それは独自のシステム環境をユーザーに押し付けるものではなく、現在のシステム環境を維持しながら更に使い易く、更に強化するものです。結果として、ユーザーが待ち望んでいたシェルに拘束されることのない自由な環境が実現されます。
     現在のシェル・プログラミング言語は、シェル・スクリプトとの独自の表現はありますが、何の言語体系にも属することのない曖昧なものです。これに対し、func 関数コマンドが創造するF言語は、C言語と相似をなすフラクタルな手続き型言語環境を提供します。それは、100%完全なコマンドの集合体からなり、C言語と同じ関数の集合体からなるインター・プリタ言語です。そのような言語構造から、最初はF言語そのものは存在しないため文法すらありません。
     解説の順序は、その基本的な考えに始まって、簡単な易しいプログラミングへと解説していきます。このシェル・プログラミングでは、C言語と同じように広域・局所変数があり、それは型を持っています。また、個々に独立した関数の集合体で構成されています。さらに、C言語と同じく前処理のプリプロセッサ機能もあります。これらについて、実際に動くプログラム例をあげながら説明を加えていきます。最後は、自由となったシェル・プログラミングにユーザー独自のコマンドを動かすことができるように、特にF言語のポインター型変数を利用した応用プログラム作成のために、C言語による組み込み用ひな型プログラム例を示しながら説明を加えます。
     なお使用に際して、その他F言語情報を配信している小生のホームページも合わせて御覧下さい。これを機会に、皆さんの自由な参加を期待しております。
     
     
     
     
     
     
     
     
     
     
    §2.やさしい入門
     何事も慣れるより慣れろです。K&R に習って、「hello, world.」を表示することから始めてみましょう。まず、mule や XEmacs などのエディタを起動し、以下のプログラムを打ち込みます。
     
       /* Display "hello, world."
          file name:  hello
          execution:  func  'hello()'
       */
     
       %main($void)
       {
          printf  "hello, world\n";  /* Linux utility */
       }

     
    打ち込みましたら、ファイル名を hello として任意ディレクトリにしまって下さい。これで用意は整いました。次は早速、実行です。
     
       func  'hello()'

     
    とキー入力しましたら、リターンキーを押して下さい。いかがです、「hello, world.」が表示されましたね。このF言語は、
      1) エディタでプログラムを書く。
      2) 名前を付けてファイルにしまう。
      3) 実行は func コマンドを使ってファイル名を指定する。
          func  'file_name()'

    の3ステップで、書き込みから実行までを済ませることができます。C言語のようにコンパイルの手間がいらないため、直ぐに結果を得ることができ、とても扱い易くなっています。
     ここで明らかとなったことは
      イ) 書式はほぼC言語と同じであること。
      ロ) コメントは /* .... */ を使うこと。
      ハ) プログラムは %main() 関数から始まること。
      ニ) 関数名は頭に % を付けること。
      ホ) 引数は頭に $ を付け、空引数は $void と明記すること。
      ヘ) コマンドの区切りは、シェルのセパレータを使うこと。
      ト) 関数は文節を {} でひとまとめにすること。
      チ) 最初の起動は、 func 'file_name(<argument>)' と入力すること。

    などです。関数名に % を付けたのは、無くても良いのですが、まれにC言語と間違えて混乱する場合があったので、ひと目で見分けられるようにしたためです。また、func の引数がファイルであるのか、あるいは関数であるのかを、オプションによらずに簡単に見分けれるようにしたためです。引数の $ も同様にひと目で引数と分かるようにしたためです。プログラムの作成は、書式がC言語に似ているため、そのままのC言語のイメージで書くことができますから、C言語さえ知っていれば簡単に書けれるようになっています。
     func は一般のコマンドと同じです。したがって、あなたが指定したファイルを見ることができます。コマンドラインの末尾に -l オプションを付けてみて下さい。
     
       func  'hello()'  -l

     
    プログラム内容がそのまま表示されましたね。この -l オプションは、プログラムを起動する前に、その内容を確認したりするのに用います。また、-x オプションは、シェルに実行させるコマンド並びを表示させるものです。
     
       func  'hello()'  -x

     
    プログラムによっては思うように動かない場合があります。そのようなとき、コメントや余分な {} を省いた実行形式に直したコマンド並びをチェックするのに使います。
     
       %main($void)
       printf "hello, world.\n";

     
     この func コマンドは、「; &」などのコマンド・セパレータを使ったコマンドラインが1行となっていて見づらく分かり難いため、それをコメント付きの縦書きにしたことに始まります。したがって、バッチ処理などのを記述するのに大変向いています。コメントには、製作した月日や製作者、および使用上の注意などを細かく書くようにします。数年経ってからこのプログラムを覗いたとき、直ぐにそれと分かると、当時を思い出したりしてとても楽しいものです。
     
     
     
    K&R
     「プログラミング言語C UNIX流プログラム書法と作法」Brian W.Kernighan/Dennis M.Ritchie 著、石田晴久訳、共立出版 のこと。UNIX の標準言語であるC言語の教科書です。

     
     
     
     
     
     
     
     
     
     
    §3.引数
     コマンドの引数は、Bash などの UNIX シェルが引数に () を伴ったコマンド・グループを認めていません。そのため、引数としてシェルが受け取ってくれるように '' や "" でくくらなければなりません。また、この影響では複雑な入れ子に制限が生じてしまい、本来の3割程度しか能力が発揮できない状態にあります。ここでは、() のコマンド・グループを認めるシェルが出るまで待ちの状態であることをご了承下さい。ちなみに、OS-9 ではこの制限はありません。
     
          func  'file_name(<argument>)'

     
     関数の引数は、通常はあまり気にする必要はなく、文字であると明示するときや、文字列の中の「,」をそのまま渡すとき "" でくくります。
     
          (<argument>) → ("arguments a, b")

     
    たとえば、次の yc ファイルを起動するとき、次のように引数を "" でくくるようにすると、「,」を含んだ文字列などすべての文字列を渡すことができます。
     
       /* Display "your comment"
          file name:  yc
          execution:  func  'yc("comment")'  */
     
       %main($call) {
          printf  "$call\n";
       }

     
    関数が引数を受ける場合は、空の $void から $call に変更して引数があることを明記します。この$から始まる引数名は、たとえば $echo などのようにコマンド名と重なっても構いません。$void は空の引数のことですから、コマンドラインから引数を渡そうとしても、受け取ることが出来ないとのエラーが返えされます。ここでは、$call の引数として受け取り、printf コマンドで出力するようにします。kterm を使っていれば日本語表示ができるので、せっかくですから実際に日本語を引数としてみましょう。
     
       func  'yc("私です!")'

     
    どうです、「私です!」と表示されましたね。また、引数の () を二重にすると実行結果を引数とすることができます。
     
       func  'yc((echo "私です!" | tr "!" "?"))'

     
    このように、引数は様々なコマンドラインを許容できるようになっています。
     次は複数の引数の場合についてです。複数の引数は、「,」を使って分離明示します。今までの引数のまとめとして、() を混在したコマンドと
     
       func  'yc("それは",  (echo "私です!" | tr "!" "?"))'

     
    そのプログラム例を示して、この章の終りとします。
     
       /* Display "your comment"
          file name:  yc2
          execution:  func  'yc2("comment1", "comment2")'  */
     
       %main($call1, $call2) {
          printf  "$call1 $call2\n";
       }

     
     
     
     
     
     
     
     
     
     
    §4.関数
     このF言語の関数は、C言語のそれと同じです。ここでは、シェル変数を利用して perl と比較してみます。
     ---- 「UNIX ツールハンドブック」 pg.293 ---
       #!/usr/local/bin/perl
     
       $foo = "dog";
       $goo = "cat";
       $hoo = "bird";
       &func($foo,$goo,$hoo);
     
       sub func {
          print  "$_[0] $_[1] $_[2]\n";
       }
     ------ エイチアイ著、ナツメ社刊 -----------
     
       /* function test.
          file name:  ft
          execution:  func  'ft()'  */
     
       %main($void) {
          foo="cat";    /* using Bash variable */
          goo="dog";
          hoo="bird";
          func  '%func($foo, $goo, $hoo)';
       }
     
       %func($_[0], $_[1], $_[2]) {
          printf  "$_[0] $_[1] $_[2]\n";
       }

     
    このF言語の関数は、このように perl の関数にみられるような sub を記述するなどの癖がありません。戻り値は中間言語としてシェルを使っているため、2つあります。1つ目は、シェルが理解することのできる文字列型(C言語の char)です。2つ目は、まれに使われることがあるそのコマンドがエラーなく終了したかどうかの終了ステータス値です。通常は文字列型を使いますから、型の明示は割愛してあります。
     このF言語は、C言語と同じく関数の集合体からなっています。それらはプロセスの集合体ですから、説明するまでもなく個々に独立しています。その結果、再帰もプログラムすることができます。ここではよく再帰に用いられる『ハノイの塔』プログラムを例に上げてみます。
     
       /* Tower of Hanoi game.
          file name: hanoi
          execution: func  'hanoi(n)'
                  n: number of discs (n≧0)
          content: move discs from pole A to pole C.  */
     
       #define  '$n - 1'  '$[$n - 1]'
     
       %main($discs) {
          printf  "\t discs: $discs\n";
          func  '%hanoi($discs, "A", "B", "C")';
       }
     
       %hanoi($n, $a, $b, $c) {
          if.z  '($n > 0)'  '{
             func  "%hanoi($n - 1, $a, $c, $b)";
             printf  "\t move #$n, from $a to $c.\n";
             func  "%hanoi($n - 1, $b, $a, $c)";
          }'
       }

     
     プログラム内から関数を実行させるには、以下のように func に続いて起動する関数名を書きます。関数起動の際、渡す引数があれば () の中に明記します。なお、() は渡す引数がない場合でも省略することはできません。
     
        func  '%function_name([<argument1, argument2, ..>])'

     
     
     
     
     
     
     
     
     
     
     
    §5.プリプロセッサ
     前章のプログラム例にも出てきたように、このF言語にもC言語と同じくプリプロセッサの機能があります。前処理によりプログラムの中にある定数やコマンドを置換して、プログラムを見易くします。この機能を使うと、たとえば Bash や perl などの独自の内部機能としてご存知の foreach も実現可能です。
     
        /* foreach simuration
           file name:  foreach
           execution:  func  'foreach()'  */
     
        #define  'foreach X'  'echo "X" | tr "," "\n"'
     
        %main($void) {
           {
              foreach "1,2,3";
              foreach "ab cde,test program";
           }  |  nl
        }

     
    foreach は、Bash や perl などの独自の内部コマンドと唱われていますが、UNIX のポリシーである「既存のコマンドを組み合わせて使うこと」によっても、このように簡単に同じ機能を実現することができます。次は、プリプロセッサを使えば三項演算子も可能であるプログラム例を取り上げてみましょう。
     
        /* Greatest common divisor 最大公約数を求める
           file name:  gcd
           execution:  func  'gcd(a, b)'
                      a,b:  some integer number  */
     
        #define  'return X ? Y : Z'  "if.z '(X)' '(Y)' else '(Z)'"
     
        %main($a, $b) {
           return  '$b == 0'  ?  'echo "G.C.D = $b"'  \
                  :  'func "%main($b, $[$a % $b])"'
        }

     
    三項演算子は、その言語(たとえばC言語)の機能のように思われるかも知れませんが、このようにプリプロセッサ機能により、このF言語でも簡単に実現可能となっています。
     このF言語はコマンドの集合体からなる自由な構造であるゆえ、プリプロセッサの定義は必ずコマンド名(ここでは return)を先に書いて明示しなければなりません。通常の小文字、あるいは大文字のみの定義とする場合は、その他の規制はありません。しかし、上のプログラム例のように小文字・大文字を混在させる場合は、大文字がプリプロセッサの中で再置換を行う特別の機能があるとの注意が必要です。また、プリプロセッサの定義は、1行に書くのを原則としています。しかしながら、記述が1行では書き切れず複数行に渡る場合は、上記例に示したように右端末尾を「\」として次の行に継続することを明示して下さい。また、これらの個々のパラメータの区切りは、その場合に応じて "" と '' を使い分けて下さい。
     ここではプリプロセッサとして #define しか取り上げていませんが、当然 #include もあります。このF言語に慣れてくると、同じ #define 定義文を使うことが多くなって来ます。その都度、#define 定義するのは面倒で非効率的です。そのようなときは #include 用として専用ファイルを作成することをお勧めします。それを仮に control.h として次のように書き込むと
     
        #define  'return X ? Y : Z'  "if.z '(X)' '(Y)' else '(Z)'"

     
    プログラムのプリプロセッサは一括して
     
        #include  <control.h>

     
    と簡略して書くことができます。このとき <> で括られているとシェルの環境変数 FUNCUF を参照します。そこにディレクトリが指定されていればその指定ディレクトリから先に #include 用ファイルを探します。一方、"" を使うとシェルの環境変数に関係なくカレント・ディレクトリから #include 用ファイルを探します。どちらを使うかは、そのときの実行条件によって使い分けて下さい。
     また、時として #include ファイルの中と重複するような定義コマンド名を使用する場合があります。このようなときは、#undef を使って定義を抹消した後、#define で再定義して下さい。
     
        #include  <control.h>
        #undef  return      /* return を抹消し*/
        #define  'return X'  'echo X'  /* 再定義する */

     
     
     
     
     
     
     
     
     
     
     
    §6.変数
     このF言語は完全なコマンドの集合からなります。コマンドはプロセスですから、個々に独立しています。このままではF言語のプログラム内で変数を使うことはできません。したがって通常は、$から始まる関数の引数を文字定数として使うことになります。それは、この章の前のプログラム例にみられる使われ方であり、いうまでもなく変数といえるものではありません。その変わりメモリ資源の消費を最小限に押えることができます。
     一方、このように独立したプロセスからなるF言語ですが、一般のプログラミング言語にみられる変数を利用することもできます。UNIX にはプロセス間でメモリを共有することのできる共有メモリの機能があります。これを使うとF言語でも変数として利用することができるようになります。このF言語では、共有メモリを余り意識せずに使えるようになっています。共有メモリを使った変数の宣言は shared を使います。関数内で用いた場合は、その関数の局所変数として利用することができます。また、%main() 関数の前に用いるとプログラム全体で有効である広域変数としても利用することができます。なお、shared 宣言は広域、局所変数共に一度しか宣言できないことに注意して下さい。
     
        /* global and local variables
           file name:  var
           execution:  func  'var()'  */
     
        shared      /* global variables */
          $a1,
          $a2[40];
     
        %main($void) {
          shared  $some;   /* local variable */
          let.z  $a1 = "hello,";
          let.z  $some = "Linux";
          func  '%output($some, $some)';
        }
     
        %output(shared *$b, shared $ptr) {  /* 文字、ポイター */
          shared  $dummy;
          echo  '\$b= $b $ptr= *$ptr'; /* ポイターを出力 */
          echo  '*$a1 *$b';   /* 広域・局所データを出力 */
        }

     
     変数の型は、中間言語として利用しているシェルが理解することのできる文字列型と、その文字が格納されている共有メモリの位置を指すポイター型の2つがあります。このF言語では、shared 宣言された $name の変数記述はポイター型となっています。その値は、関数の起動時に決められ、共有メモリという限られた範囲を利用することから、原則としてポイター型の値は操作変更してはいけません。そのポイター型が示す共有メモリ域の格納内容は、その shared 定義した範囲内で自由に変更することができます。"a b c" も "1 2 3" も文字ですが、それらを文字として処理するのか、それとも数字として処理するのかの判断は、個々に自由です。利用状況に応じて対応されるとよいでしょう。
     関数の引数として用いる場合の変数定義は、その関数内で定義して用いるそれとは機能が少し違います。ポイター型で渡された引数を関数で受けるとき、
     
       %function(shared $variable) {  →  ポイター型のまま受ける
       %function(shared *$variable) {  →  文字列型として受ける

     
    のように引数を受け取ることができます。このとき、shared 宣言したことにより、その関数内に新たに共有メモリ域が確保され、渡された引数が格納されます。関数内の独立した新たな変数となります。特に「*」を付けた変数はその関数独自の文字変数となりますから、起動する親関数の変数に影響を与えないため、自由に加工・変更することができます。
     関数内プログラムにおいて *$name とする記述は、shared 宣言した引数の内容を示すことができます。ただし構造上、その変数値は関数起動時の値が一律となることに注意して下さい。
     
       let.z  $abc = "Linux";
       func  '%function($abc)';
          ↓
       %function(shared *$v) {
          let.z $v = "change";
          echo  '*$v';   /* 起動時の "Linux" が表示される */
          fprintf '("\$v = %s\n", $v)';   /* "change" を表示 */
       }

     
     変数の大きさはデフォールトのとき 128 バイトの共有メモリ域が割り当てられます。また、構造体の概念がない変わりに、任意サイズのデータを自由に格納してプロセス間で利用できるように可変となっています。それは、整数値を使って次のように設定することができます。
     
       $a1     → デフォールト 128 バイト
       $a2[40]   → 40 バイト
       $v[2k]   → 2K バイト
       $z[5m]   → 5M バイト

     
     このようにF言語の変数は、1つのプロセスからなるC言語プログラムのような狭義のプログラムと、複数のプロセスからなる広義のプログラムで用いる2通りがあります。狭義のプログラムで用いる変数は、ループ構造の繰り返しで用いられます。そのままメモリに直接アクセスして変更したり、取り出したりして利用します。これに対して広義のプログラムでは、再帰を用いるのを原則とし、変数はいわゆる文字定数として利用します。このF言語は広義のプログラム言語ですから、狭義のデータ構造をそのまま持ち込むことはできません。ここでは、この狭義のデータ構造をシミュレートするために共有メモリを駆使して実現しています。そもそもその共有メモリ自身に制限が有り、それが変数の概念に反映されてしまうことは避けられません。
     
     
     
     
     
     
     
     
     
     
    §7.制御構造
     何度も述べているように、このF言語は完全なコマンドの集合からなります。したがって、if や while などの制御構造もその例外ではありません。今までのスクリプトといえば、シェル自身が独自の制御構造を持っていて、その制御構造にしたがってプログラムしなければなりません。しかし、このF言語では、従来のスクリプトでは必須となっている制御構造は存在しないのです。「それでは、プログラムできないのでは?」と思われるかも知れません。ご安心下さい。制御構造はユーザが個々に作成し、それを元にプログラムの流れを制御するのです。このとき、シェルは一切関係ありません。
     現在のシェルは、コンピュータの利用環境が複雑高度となるそれに比例する形で改良が加えられ、新たな名前のシェルとして進展してきています。コンピュータの利用は、今後も更に複雑多様化するのは誰がみても明らかです。それゆえ、いずれまたユーザ・インターフェースを改良するために新たな名前のシェルが出てくるであろうとの予測が可能です。これでは、将来もシェルに振り回されるのは当然といえます。ここでは、この昔のままの発想を改め、機能を分散して必要な機能を逐次追加できるようにしてみてはどうか、という提案なのです。中でも if などの制御構造は様々なパターンが考えられ、到底1つの制御構造で処理するには無理があるその代表といえます。
     
       /* Using IF control utility command.
          file name:  prg_if
          execution:  func  'prg_if(file_name)' */
     
       #define  /nil  /dev/null
     
       %main($file) {   /* if.z はコマンドです! */
          if.z  -s  '((func %select($file)) != 0)'  '{
             echo  " Can not find your <$file> file!!";
          }'  else  '{
             echo;
             more  $file;
          }'
       }
     
       %select($name) {
          echo " You select the <$file> file.";
          ls $file >/nil;
       }

     
    これは、あるファイルを more で参照するプログラムです。制御コマンドの if.z は、%select() 関数を実行し、エラーがなければ more 参照し、エラーならばその旨表示して終る流れを演出しています。終了ステータスによって制御の流れを変える役割を担っています。当然、人それぞれですから「見づらい」とか「ダサイ」とか思われるはずです。ここでは、無理やり if.z の文法を押し付けるつもりはありません。気に食わなければ個々に作成してそのコマンドを使えば良いのです。
     この制御は、将来もこのまま使えるとは思えず、より機能が高度化すると思われます。そのとき、このコマンドとして自由に取り換えられる利点は、効率的であると同時に理にかなっているいえます。シェルをまるごと取り換える必要はありません。その時点で改良が必要な部分だけを取り換えれば良いのです。
     ところであなたは、「コンピュータに命令を与えて実行させる」ことを行っていませんか?この文の流れを考えてみて下さい。それは
     
       while  (コマンドを選んで)  {
          commands;     /* 実行させる */
       }

     
    と記述できることをご存知でしょうか。今はビジュアル・シェルが当り前になっていて、それはひとまとめのシェルであるとするのが常識となっています。でも、このF言語の考えでは、この while の部分だけを作れば良いのが分かります。実際、OS-9 のフロント・エンドは while コマンドで作られ、CUI ながらも、コマンドを打ち込むことことないスケルトン(透過)な環境が実現されています。要するに、while はそのコンピュータ・システムのフロント・エンドの性格があり、それは GUI とするマウス制御としても良いし、CUI としても良く、シェルとは独立して開発することができるのです。そしてそれは、ユーザーの自由です。
     
     
     
     
     
     
     
     
     
     
    §8.コマンドの作り方
     F言語に使われるコマンドは、通常のコマンドに変わりありません。これといった特別のテクニックを必要とするものでもありません。まず、制御コマンドの if.z を例に説明します。
     
        if.z  [<option>]  (<control>)  (<commands>)
           [else  if  (<control>)  (<commands>)]
              [else  (<commands>)]

     
    コマンド名 if.z を頭に「オプション、制御、実行、[else if、制御、実行]、[else、実行]」となっています。制御や実行文は、() でくくりますが、この () はコマンド・グループのことです。() を使う訳は、() を {} とすれば明らかなようにC言語の if 構文と同じになるからです。func 関数コマンドは、シェルが () のコマンド・グループを許容することを前提に、ファイルを読み込むと直ちに {} を () に変換しています。Bash の引数は '' や "" でくくることになっていますが、それは無指向性であるために入れ子構造にすることができません。しかし、() はその指向性から入れ子構造に制限がなく、C言語で用いられていることからも実証済みです。それゆえ強要はしませんが、構文となる引数は将来をも見据えて () でくくることをお勧めします。
     shared 宣言した $ から始まる変数は、間にキャンセル・コード(CAN)の識別子を入れて、共有メモリの shmid とそのオフセット値の組み合わせで構成されています。これをF言語のポインター型と定義しています。
     
       $,CAN,12345abc,67890def → $,CAN,shmid,offset

     
    shmid や offset 値はアスキー表示されますが、32ビットの4バイト枠をいつも使い切っている訳ではありません。ここでは、Cプログラミングの負担を軽減するために、アスキー表示枠(AREA)は17バイトの定数を用いています。けれども、値が小さなときは余りの部分が出てきてしまいます。そこで、これを補うために CAN で埋めています。以上が変数の内部構造です。ではC言語でコマンドを作る場合は、その都度表示に合わせるようにしなければならないかというと、そのようなことはなく、それは以下のように定書式となっています。
     
       #define  AREA  17    /* the F pointer size */
       #define  CAN  '\x18'   /* cancel code */
     
       char
         *shmaddr,   /* pointer of shared memory */
         *ptr;     /* user access data pointer */
       int
         offset,     /* from shmaddr header */
         shmid;     /* segment ID of shared memory */
     
       int job_sscan(int *, char *);
       int get_ptr(char *, int);
     
       int main(int argc, char *argv[])
       {
         char *your_ptr,*p;
         .... 中略 ....
         if (*(p++) == '$' && *p == CAN) {
           offset = job_sscan(++p, &shmid);
           if ((shmaddr = shmat(shmid, NULL, 0)) == (char *)-1)
             exit(0);
           your_ptr = &shmaddr[offset];
           .... your_ptr を使って読み書き処理をする ....
           shmdt(shmaddr);
         }
         .... 中略 ....
         return 0;   /* 又は exit(0)、必ず戻り値を明記すること */
       }
     
       int job_sscan(int *temp_shmid, char *p)
       {
         *temp_shmid = get_ptr(p, AREA - 8);
         return get_ptr(p + AREA - 8, 8);
       }
     
       int get_ptr(char *p, int length)
       {
         char *t,mybf[AREA + 1];
         t = mybf;
         while (--length >= 0) {
           if (*p != CAN)
             *t++ = *p;
           p++;
         }
         *t = '\0';
         sscanf(mybf, "%x", &length);
         return length;
       }

     
    このCプログラムを新たに作ろうとするプログラムに組み込み、(char *)your_ptr の指すポインター位置から読み書きして下さい。
     後のF言語プログラムを制御するために、Cプログラムは正常終了したのか、それとも異常終了したのかを明確にシェルに返すようにして下さい。このとき、
     
       ステータス
        0    → 正常終了
        1〜9  → if,loop の制御値用
        10以上 → 通常のエラー用

     
       trap 割り込みシグナル
        0    → 正常終了(EXIT)
        1    → ハングアップ(SIHUP)
        2    → del 割り込み(SIGINT)
        3    → ctrl+\ 終了(SIGQUIT)
        9    → 強制終了(SIGKILL)
        14   → アラームクロック(SIGALRM)
        15   → ソフトウェア終了(SIGTERM)
     
    として他のユーザーが混乱することのないよう、統一して下さるようお願いします。
     また、if などの制御コマンドは多くのバリエーションがあると思います。同じ if などは、作者を尊重する意味から if.izumi のようにサフィクスを付けて下さい。この func 関数コマンドの模造はいけませんが、func コマンドを使ったF言語プログラムや、その他 if,while などのコマンド作成はユーザの自由です。皆さんのプログラム作成のお役に立てれるように、if.izumi/F の「/F」の付いた見本プログラムを
        http://www.vector.co.jp/
    に掲載しておきましたのでご利用下さい。応用ソフトができましたら逐次公開する予定です。なお、現在の Bash などでは、このF言語(Linux)の2〜3割程度しかパワーを引き出すことが出来ていないため、OS-9 標準シェルのように、() のコマンド・グループの引数を認めるコンパクトなシェルが欲しいところです。
     
     
     
     
     
     
     
     
     
     
    あとがき
     今まで、「シェルは一種のプログラミング言語であって、その言語の約束にしたがって書かれたプログラムをシェルが実行するものである」と考えられていました。また、この考えに何等疑問を持つ人は誰もいませんでした。何故なら、開発元の技術者等がこの考えを押し進めていたため、凡人であるユーザーの入り込む余地がなかったからです。しかしながら、この度偶然にもこれに割り入る機会を得ました。その結果、当初予想もしなかった成果を得ることができました。
     ここで皆さんに提供する新しい環境は、この従来の「シェルがプログラムを実行する」という考えではなく、「シェルにプログラムを実行させる」という考えによるものです。「シェルがする」の能動態に対し、「シェルにさせる」という反対の受動態によって実現しています。これは無意味な言葉の遊びと受け取られるかも知れません。けれどもこのF言語で明らかとなったように、「する」とすれば、今までのようなシェルに拘束された環境が構築され、ユーザが入り込む余地はありません。一方、「させる」とすれば、間接的となってこのようなシェルに拘束されることのない環境が構築され、ユーザの自由が確保・保証されるのです。また、それは UNIX のポリシーである「ある作業を行う際に既存のプログラムを組み合わせて使うこと」を更に推進するものでもあります。
     このF言語の哲学は陰と陽の排反にあります。つまり、この元となる考えは、コンピュータの原理である0と1の2進法であり、その数値が持つ数学・集合論あるいは確率論の排反事象を基本にしています。現在のユーザーを無視した、特定企業が暴利を得ているコンピュータ・システムの入口、つまり原点に立つとき、その入口であるシェルをコンピュータ・システムの原理に習って現在の排反とすることでその問題に対処しようとするものです。ここに Free な言語Fが産まれ、UNIX のポリシーであるソフトウェア資産の継承を更に押し進めると同時に、更にユーザの自由をも提供するものです。その自由とは、民主主義のそれと同じです。ルールがなければ無法と化することでしょう。個々に良識あるマナーを持ってこれに接して欲しいものです。

    26.July, 2000        
    開発者  和泉 博     
    mail:  hero.izumi@nifty.ne.jp