シェルスクリプト言語 kitutuki

1.文法

1.0.0 １コマンド

    コマンド名 引数1 引数2 ...

    を一つのコマンドを実行します。引数の数は任意です。
    コマンドと引数の区切りにはスペースとタブが使えます。
    実行するコマンドはディスクに保存されたプログラムが使用できます。コマンドを検索するパスには環境変数PATHが使用されます。例えばPATHに/bin:/usr/binが入っていれば（:がPATHの区切りです。例えばexport PATH=/home/hoge/bin\:/home/hoge2/binと設定します。kitutukiでは:は特別な意味を持つのでクォートが必要です)

    >ls -al

    と実行すると/bin/lsと/usr/bin/lsが検索され先に検索された/bin/lsが実行されます。
    
    引数には慣習的に-で始まるオプション, --で始まるロングオプションがあります。ロングオプションは-lといった一文字のオプションが覚えにくいという理由から覚えやすい--listなどと分かりやすいオプション名で使えるようになったものです。
    引数の書き方としては、lsなど一般的な外部コマンドでは-a -lと二つオプションをつけることと-alと一つのオプションをつけることは同じ意味として処理されます。ただ残念なことにkitutukiの組み込みコマンドでは-alは-aと-lとは見なされません。-a -lと分けて書いてください。またオプションに引数を取るコマンドでは一般的に-T 4と書くことと-T4と書くことは同じですが、kitutukiの組み込みコマンドでは-T 4と書いてください。

1.0.1 標準入力、標準出力、エラー出力とリダイレクト

    一つのコマンドには入力元、出力先、エラー出力先があります。例えばvimという文章編集コマンドを実行しているときは、入力元はキーボード、出力先は画面、エラー出力先も画面です。エラー出力というのはコマンドに不具合があった場合にそれを知らせる特別なメッセージを表示する場所のことです。UNIXではこの入力元や出力先を設定によってファイルに変えることができます。それがリダイレクトです。kitutukiには以下のリダイレクトがあります。

    1>> ファイル名            標準出力をファイルに追記
    1> ファイル名             標準出力をファイルに書き込む
    2>> ファイル名            エラー出力をファイルに追記
    2> ファイル名             エラー出力をファイルに書き込む
    >> ファイル名             標準出力をファイルに追記
    > ファイル名              標準出力をファイルに書き込む
    2>&1                      エラー出力を標準出力として扱う（両方標準出力に流す)
    &>                        上と同じ
    < ファイル名              ファイルを標準入力としてコマンドに渡す

    例

    puts ファイルに書き込む文字列 > file1

    とするとfile1に「ファイルに書き込む文字列」という文字列が書き込まれます
1.0.2 １文

    コマンド1 引数... リダイレクト... | コマンド2 引数... リダイレクト...

    とコマンドを | でつなげたものが一文です。コマンド1の出力先がコマンド2の入力先として扱われて、また、そのコマンド2の出力先が次のコマンドの入力先として扱われ、、、と最後のコマンドまで実行すれば最後のコマンドが画面に出力します。
    これをパイプでつなぐといいます。

    文末には以下の区切りが使えます。

    ;               普通の文の区切り
    改行            普通の文の区切り
    &&              次の文は文のリターンコードが0(実行成功を表す)でないと実行されません
    ||              次の文は文のリターンコードが0以外(実行失敗)でないと実行されません
    &               文をバックグランド実行します

    また変数の展開は文を単位で行われます。

1.0.3 バックグラウンド処理
    文は&をつけるとバックグランド実行となります。ただし組み込みコマンドやユーザーコマンドの実行は並列的に行うことができません。有効なのは外部コマンドだけです。例えばdefはユーザーコマンドを定義するコマンドですが

    > def fun { sleep 10 }
    > fun &

    はバックグランド実行できません。

    > def fun { sleep 10 & }
    > fun

    は可能です。

    jobsでバックグラウンドや実行中サスペンド(CTRL-Z)したプロセスグループの一覧が見れます。

    fg jobsの一覧の番号

    でそのプロセスグループをフォアグランドに持ってくることができます。

    bg jobsの一覧の番号

    でそのプロセスグループにSIGCONTが送れます。つまり停止したプロセスを再開できます。停止する場面は例えばcpでコピー中にCTRL-Zした場合です。この場合bgするとバックグラウンドでコピーの続きをやってくれます。

1.1 クォート

    クォートとは引用を表し引用符で括るなどとして他の文として解釈されないようにするためのものです。使っている特別な記号を単なる文字列として処理したいときに使います。
    kitutukiには一文字のクォートとシングルクォートとダブルクォートがあります。一文字のクォートは\で、この後に来た記号や文字はkitutukiで特別な意味を失います。例えば行末を表す;はそのままでは行末としてkitutukiが処理しますが、\;とすると;はその意味を失い文字列として処理します。以下は実行例です。> の行が実行文。その下が実行結果です。putsは組み込みコマンドで引数の文字列を表示します。

    > puts 一つ目の文字列を表示; puts ２つ目の文字列を表示
    一つ目の文字列を表示
    ２つ目の文字列を表示

    > puts 一つ目の文字列を表示\; puts ２つ目の文字列を表示
    一つ目の文字列を表示; puts ２つ目の文字列を表示

    \自身を使いたい場合は\\を使います。

    > puts これが\\文字です
    これが\文字です

    シングルクォートは一つ目の'から２つ目の'までを文字列として扱います。

    > puts '一つ目の文字列; puts 二つ目の文字列'
    一つ目の文字列; puts 二つ目の文字列

    ダブルクォートも同じです。

    > puts "一つ目の文字列; puts 二つ目の文字列"
    一つ目の文字列; puts 二つ目の文字列

    シングルクォートやダブルクォートで囲まれた文字列の中でシングルクォートやダブルクォートを文字として使いたい場合はクォートしてください。

    > puts "シャア \"赤い彗星\" アズナブル"
    シャア "赤い彗星" アズナブル

    シングルクォートやダブルクォートの違いはダブルクォートは中で変数を展開する点です。変数は文字列を格納する入れ物です。

    > futatuna="赤い彗星"
    > puts "シャア \"$futatuna\" アズナブル"
    シャア "赤い彗星" アズナブル

    > futatuna="赤い彗星"
    > puts 'シャア "$futatuna" アズナブル'
    シャア "$futatuna" アズナブル

    変数以外にも$()によるコマンド置換も同じ規則です。

1.2 特殊文字列

    kitutukiではラインフィールドやキャリッジリターン, タブなどを埋め込めます。
    \n ラインフィールドとして扱う
    \r キャリッジリターンとして扱う
    \t タブとして扱う

    printは文字列を表示するコマンドです。putsと違い最後に改行しません。

    > print "初めまして\nこんにちは\n"
    初めまして
    こんにちは

    > print 初めまして\nこんにちは\n
    初めまして
    こんにちは

    > print 初めまして\\nこんにちは\\n
    初めまして\nこんにちは\n

1.3 算術演算

    足し算や掛け算など数値計算を行います。書式はbashと同じく$((計算式))です。
    使える演算子は

    (計算式)          計算順の優先順位を変える
    pai               3.14...を表す
    log 計算式
    int 計算式
    ceil 計算式
    floor 計算式
    log10 計算式
    abs 計算式
    exp 計算式
    sin 計算式
    cos 計算式
    asin 計算式
    acos 計算式
    atan 計算式
    sinh 計算式
    cosh 計算式
    sqrt 計算式

    式 ** 式 指数計算
    式 * 式  乗算
    式 / 式  除算
    式 % 式  除算の余りを得る
    式 + 式  足し算
    式 - 式  引き算

    上記は優先順位順です

    > puts $((1.1 + 1))
    2.1

    > a=1.1
    > puts $(( $a * 3 ))
    3.3

    > puts $(( log 3 + 3 ))    # log 6と同じです
    1.791759

    > puts $(( (log 3) + 3))
    4.098612

1.3.1 変数
    変数にはローカル変数、グローバル変数、環境変数があります。

    ローカル変数はスタックを使った普通の変数です。関数が呼ばれるごとにフレームが積まれ、関数から抜けるごとにフレームが除去されます。
    代入方法はa=1として=が混じったコマンド名を使い代入します。注意してほしいのはa = 1とは書けないことです。なぜならa = 1とするとaというコマンドに=と1という引数があることになるからです。
    参照方法は$aと変数名に$をつけてください。${a}でも可です。また$$aというのもあります。この違いですが${a}は単に表記上の問題のためにあります。$aと変わりがありません。$$aと$aの違いは、$aの場合kitutukiが使う特殊文字があればクォートします。$$aはクォートしません。

    >a="a;b"; puts $a
    a;b

    >a="a;b"; puts $$a
    command not found

    となります。$aの場合はputs a\;bと展開されるのに対し$$aの場合はputs a;bと展開されてbというコマンドがないというエラーがでます。

    > a=1; puts $a
    1
    > b=b; puts a${b}c      # puts a$bcとすると$bcという変数になってしまう
    abc
    > a=1; i=a; puts ${$i}    # perlのように$$iは使えません
    1
    > a=1; def fun { a=2; puts $a; }; fun; puts $a
    2
    1
    > var a=1; puts $a
    1
    > ls / | var a b c; puts $a $b $c     # パイプでデータを受け取れます
    bin boot cdrom

    グローバル変数は関数内や関数の外でも使える変数です。
    関数の外でa=1などとするとグローバル変数が作成されます

    環境変数はkitutukiから起動したプロセス中でも参照できる変数です。

    変数の参照時の優先順位は

    ローカル変数 > グローバル変数 = 環境変数 = 配列 = 連想配列

    となっています。
    というかグローバル変数と環境変数と配列と連想配列は
    同じ名前で一つを定義すると他は消されます

    変数名には英数字と_が使えます。環境変数はアプリケーションによっては記号が使えないことが問題になるかもしれません。

    変数とシングル、ダブルクォートの関係ですが上で書いたとおり、ダブルクォート中は変数の展開をして、シングルクォート中は変数の展開はしません。

    >a="a;b"; puts $a
    a;b

    >a="a;b"; puts '$a'
    $a

    >a="a;b"; puts "$a"
    a;b

    (以前のバージョンでは"$a"はa\;bを返しましたが仕様を変更してa;bを返すようになってます。)

    変数には添え字があって、部分文字列を得ることができます。

    >a=abc; puts $a[0]
    a

    >a=abc; puts $a[0..1]
    ab

    >a=abc; puts $a[0..-2]
    ab

    >a=abc; puts $a[0,0,0,1]
    aaab

    >a=abcdefg; puts $a[0..1, 3..4, 0]
    abdea

    あと変数には配列があります。配列はグローバル変数と同じで各関数共通に使うことができます。（ローカル変数としては扱えない）
    配列は$配列名で配列の全ての要素を空白区切りで貼り付けます。$配列名(,)で区切り文字を,に変えたりすることができます。$配列名[インデックス]で要素にアクセスできます。$配列名_sizeで配列のサイズにアクセスできます。

    > ary_new A
    > A[0]=a        # ary_new A a b c でも良い
    > A[1]=b
    > A[2]=c
    > puts $A[0];
    a
    > print $A[1..2]
    b
    c
    > print $A[1,2]
    b
    c
    >print $A[0, 2, 2, 0..-1]
    a
    c
    c
    a
    b
    c

    >print $A[2..1]
    c
    b

    > print $A
    a
    b
    c
    > puts $A(,)
    a,b,c
    > puts $A_size
    3
    > puts $A[1..2]( )
    b c

    > cat main.c | ary_new A    # 配列Aに各行が要素として格納されます
    > puts $A[0..1]
    #include <stdio.h>
    #include <stdlib.h>

    もう一つ変数にはハッシュ（連想配列)があります。連想配列も配列と同じでローカル変数としては使えません。

    連想配列にはすべての文字がキーに使えます。(アスキー文字以外にUTF-8文字も使えます)

    >hash_new a
    >a[a]=abc
    >a[あ]=def
    >puts $a[a]
    abc

    >puts $a[あ]
    def

    >print $a
    a
    abc
    あ
    def

    >puts $a(,)
    a,abc,あ,def

    >puts $a | lines \*2+0
    abc  # 値をとる
    def

    >puts $a | lines \*2+1
    a     # キーをとる
    あ

1.3.5 リターンコード(終了コード)

    コマンドを実行したら、そのリターンコードがローカル変数$RCODEに入っています。

    >true; puts $RCODE
    0

    >false; puts $RCODE
    1

    リターンコードにはコマンド実行中にエラーがあった場合に0以外の数値が入力されます。

    ! 文(コマンド 引数 ... | コマンド ....)

    コマンドを実行してリターンコードを反転する。
    文のリターンコードが０以外ならなら０に０なら１にします。

    > ! puts aaa\nbbb | match -q a; puts $RCODE
    1

1.4 コマンド置換

    コマンド結果をコマンドに貼り付けます。書式は$(複文) $$(複文)

    >$(print "puts aaa")
    command not found

    >$$(print "puts aaa")
    aaa

    上はputs\ aaaが貼り付けられるのでエラー
    下はputs aaaが貼り付けられるのでputs aaaが実行される

    範囲指定による部分文字列の取り出しも可能です

    >puts $(ls)
    AUTHOR

    >puts $(ls)[0..2]
    AUT

1.5 チルダ展開

    ~は$HOMEが貼り付けられます

1.6 グロブ

    カレントディレクトリを基点としてグロブにマッチするファイル名を貼り付けます。

    *                       任意の文字列
    ?                       任意の一文字
    []                      文字列クラス。[abc]ならaかbかcか。[a-z]ならa-z全部。[^a]はa以外。

    > puts [^mM]*
    a a.c aaa autom4te.cache bin conf10097.dir conf11225.dir conf15278.dir conf22554.sh conf9022.dir config.cache config.h config.h.in config.log config.status configure configure.in doc install.sh libkitutuki.so.1.0 kitutuki kitutuki.c kitutuki.c.bak kitutuki.c.bak2 kitutuki.h kitutuki.o kitutuki.sao kitutuki.sh test.c tmp todo.txt usage.en.txt usage.ja.txt

    カレントディレクトリからm, M以外で始まるファイル名を表示

    注意してほしいのは.*は.から始まるファイル名にマッチしますがkitutukiでは.と..にはマッチしません。

    グロブのフィールドの区切りを変更することも可能です。

    > puts *.c
    a.c b.c c.c

    > print *.c(\n)
    a.c
    b.c
    c.c

    > print *.c(,)
    a.c,b.c,c.c

1.7 グローバルパイプ

    最後にコマンド名を書かずにパイプで終わるとグローバルパイプに書き込まれます。逆にコマンド名を書かずにパイプで始まるとグローバルパイプから読み込まれます。書き込んだ後読み込む前に別の処理を行っても大丈夫です。

    > ls | match -g . | join , | 
    > lv main.c
    > sleep 10
    > | less                      # ls | match -g . | join ,の結果がlessされる

    一度書いたグローバルパイプは何度も読み込めます。

    > echo aaa |
    > | print
    aaa
    > | cat
    aaa

1.7 ファイルハンドル
    
    <ファイル名>でファイルから一行読み込みます。続けて<ファイル名>で次の行が読めます。ファイルはオープンされているので、処理が終わるとclose ファイル名でファイルを閉じなければなりません。ファイルの終端まできたらリターンコード1を返します。STDINは特別なファイル名で現在の標準入力を表します。これは例えば関数やパイプの中などコンテキストによって適切に処理されます。

    > head main.c
    #include "kitutuki.h"
    #include <string.h>
    #include <strings.h>
    #include <stdlib.h>
    #include <locale.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <glob.h>

    > <main.c>
    #include "kitutuki.h"
    > <main.c>
    #include <string.h>
    > close main.c
    main.c is closed

    >cat main.c | while <STDIN> | { | length | add \n } | less

    # グローバルパイプとの連携です。<STDIN>はcat main.cの出力を一行ずつ読み込みグローバルパイプに書き込みます。グローバルパイプから一行ごとにlength | add \nが受け取り、その行の長さと改行を付与したものを出力します。ファイルの終端まできたらリターンコードは1を返すのでwhileのループを抜け、while内で実行された全ての出力結果はlessが受け取ります。

    注意してほしいのは、この書式があるために<file1>file2というリダイレクトは< file1 > file2と空白を入れて書かないといけないということです。

    <>は<STDIN>と同義です。

    >puts aaa\nbbb\nccc | while <> | { | chomp | add D\n }
    aaaD
    bbbD
    cccD

1.7 ブロック

    ブロックには中括弧によるブロックとインデントによるブロックがあります。
    インデントによるブロックは:によって始まります。

    > def fun { print エフェメラ; puts クルツ }
    > def fun2:
        print アンディ
        puts クルツ

    defなどの特別な構文でなくコマンド中にでてくるブロックは文字列と同義です。

    > puts { aaa }
    { aaa }

1.7 サブシェル

    ()で括ったコマンドを一つのグループとして扱います。kitutukiのサブシェルは新たにkitutukiを実行してそこでコマンドを実行させているわけではありません。新たにプロセスを作ってはいません。

    > (print aaa; print bbb) | more
    aaabbb

    > if ((true || flase) && true) || false { puts yes }
    yes

1.8 制御文

    if文
    if 条件文 実行文 elif 条件文 実行文 .... else 実行文

    > a=1; if [ $a = 1 ] { puts yes } else { puts no } | uc
    YES

    > a=1; vim a.sh
    if [ $a = 1 ]:
        puts yes
    else:
        puts no
    > load a.sh
    yes

    while文

    while 条件式 実行文

    > i=0; while [ $i -lt 3 ] { puts $i; i=$(($i+1)) }
    0
    1
    2

    > vim a.sh
    max=0
    cat main.c | while <> |:
        | length | var len

        if [ $max -lt $len ]:
            max=$len

    puts "main.cの最大行は$max文字です。"

    > load a.sh
    main.cの最大行は78文字です。

    while文はbreakやCTRL-Cで割り込むことで抜けられます。

    def文

    def 関数名 ブロック

    > def fun { puts $ARGV_size; puts $ARGV[0] $ARGV[1]; puts $ARGV(\n) }; fun a b c
    3
    a b
    a
    b
    c

    >vim a.sh
    def fun:
        a=2
        global b=3
        puts $a $b

    > load a.sh; a=1; global b=2; (pust $a $b; fun; puts $a $b) | less
    1 2
    2 3
    1 3

    関数内では引数はARGVという配列に渡されます。また中で宣言されたローカル変数(varによる宣言やa=1など)は関数から抜けるときに自動的に消えます。

    特殊な構文として引数名を指定することができます。

    > def fun(_ARGV) { puts $_ARGV_size; puts $_ARGV(,) }; fun a b c
    3
    a,b,c

    このとき注意してほしいのはこのように引数が指定されたユーザー関数funが呼ばれた時に関数用にスタックは作られないということです。つまりfun内では外のローカル変数にアクセスできますし、fun内で定義したローカル変数は外でも生きています。これはブロックを使ったユーザー定義の制御構文の定義に使用します。

    > vim kitutuki.sh
    def each(_ARGV):
        NR=1
        while <STDIN> |:
            eval $_ARGV[0]

            NR=$(($NR + 1))

   このように定義するとeachに渡すブロックで外で定義されたローカル変数にアクセスできます。

   > a=1; ls -al | each { | var line; puts "$a:$line"; a=$(($a + 1)) }; puts $a

   注意) 以前はこの文法でeachというユーザー関数を提供していましたが、あまりにもパフォーマンスが悪いために廃止しています。

1.9 ブレース展開

    a{{aa,bb}}c --> aaac abbc
    {{01..10}} --> 01 02 03 04 05 06 07 08 09 10
    {{1..10}} --> 1 2 3 4 5 6 7 8 9 10

2.0 プロセス置換

    <(複文) 

    コマンドの結果をファイルとして扱う

    > cat <(echo aaa) <(echo bbb)
    aaa
    bbb

    > diff <(ls ./etc) <(ls /etc)

    ls ./etcとls /etcの両方の出力がdiffされる

    >(複文)

    ファイルとして扱ったコマンド結果をコマンドとして扱う

    > ls | tee >(grep main | less) | less

    ls の結果がlessされてから、ls | grep mainの結果がlessされる

    kitutukiのプロセス置換はパーサーによる変換だけで処理されます。
    >(), <()のところがtmpに作られる一時ファイルに置き換えて
    色々文も追加して処理させています。
    bashやzshとは実装方が違うので動作に違いがあるかもしれません。
    kitutukiの実装による利点は端末制御を行うコマンドも>()の中に
    書ける点です。

2.0.5 インタラクティブシェル
    
    bashと同じくreadlineを使っているのでbashと同じような使用方法が使えます。特徴としてはmigemoによる日本語ファイル名の補完を行う点です。

    > ls
    UTF8時代だから日本語ファイルを使おう エフェメラクルツ

    > ls efe[TAB]
    > ls エフェメラクルツ

    と補完されます

2.1 組み込みコマンド

    msleep

    sleepと同じく実行を止めます。画面にアニメーションが表示されます。

    true

    リターンコード 0を返します

    false

    リターンコード 1を返します

    [

    条件判定式を実行します。詳しくはman [してください。ただしbashなどの[の全ての条件式を実装しているわけではありません。
    kitutukiの[の特徴としては[ 文字列 -re 正規表現 ]で正規表現の条件式が使えるところです。マッチした場合以下の特殊なローカル変数を使います。

    $PREMATCH  マッチした文字列の前の部分が入る
    $MATCH マッチした文字列が入る
    $POSTMATCH マッチした文字列の後の部分が入る

    >[ abcdefg -re c ]; puts $PREMATCH,$MATCH,$POSTMATCH
    ab,c,defg

    グループ化した文字列は$数値に入ります。

    >[ abcdefg -re '(.+)(c)(.+)' ]; puts $1,$2,$3
    ab,c,defg

    あとkitutukiには文字列の大小比較があります。

    -slt

    左側の文字列が小さいなら真

    -sle

    左側の文字列が小さいか等しいなら真

    -sgt

    右側の文字列が大きいなら真

    -sge

    右側の文字列が大きいか等しいなら真

    -silt

    左側の文字列が小さいなら真(大文字小文字は同じ物として扱う)

    -sile

    左側の文字列が小さいか等しいなら真(大文字小文字は同じ物として扱う)

    -sigt

    右側の文字列が大きいなら真(大文字小文字は同じ物として扱う)

    -sige

    右側の文字列が大きいか等しいなら真(大文字小文字は同じ物として扱う)

    ほかにもkitutukiの[は文のパイプの２番目以降で実行されると標準入力から文字列を受け取って、その文字列を対象文字列の引数として判定することができます。

    >print aaa | [ = aaa ]; puts $RCODE
    0

    >print aaa | [ ! = aaa ]; puts $RCODE
    1

    この条件式は特別な構文ではなく組み込みコマンドなので書き方に注意してください
    [aaa=aaa]  ->[aaa=aaa]というコマンドは無いのでだめ

    [ aaa=aaa ] -> 第二引数がaaa=aaaとなるのでだめ

    [ aaa = aaa] -> 第３引数がaa]となるのでだめ
    [ aaa = aaa ] -> ok

    index 対象文字列 検索文字列
    index -I 検索したい文字列
    | index 検索したい文字列

    rindex 対象文字列 検索文字列
    rindex -I 検索したい文字列
    | rindex 検索したい文字列

    文字列から検索したい文字列が現れる場所(文字列の先頭からのインデックス)を出力します。文字列のインデックスは0からです。rindexは検索を右から左に行います

    -q 判定だけして出力をしない。リターンコードの値だけを見たいときに使います。

    -l 出力の最後に改行を加える

    -I 標準入力から文字列を受け取る(kitutukiの組み込みコマンドではパイプの二番目以降にコマンドが実行されると自動的に-Iがついたものとして処理されます)

    -s 入力のテキストのエンコードをsjisとして処理します

    -e 入力のテキストのエンコードをeucjpとして処理します

    -w 入力のテキストのエンコードをUTF8として処理します

    -c 数値 検索回数の指定。2が指定されると２回目のマッチした位置が返されます

    -n 数値 文字列の検索開始位置を指定します。

    length 文字列
    length -I
    | length 

    文字列の長さを出力します。

    -I 入力された文字列の長さを返します。(パイプの二つ目以降にコマンドがある場合は必要ありません。)
    -l 改行を出力します。
    -s 文字列のエンコードをSJISとして数えます。
    -e 文字列のエンコードをeucjpとして数えます。
    -w 文字列のエンコードをUTF8として数えます。
    -t ターミナルでの文字列の幅を返します。

    uc
    uc -I
    | uc

    文字列を大文字に変換します

    -I 入力された文字列を処理します。(パイプの二つ目以降にコマンドがある場合は必要ありません。)
    -l 改行を出力します。

    lc
    lc -I
    | lc

    文字列を小文字に変換します

    -I 入力された文字列を処理します。(パイプの二つ目以降にコマンドがある場合は必要ありません。)
    -l 改行を出力します。

    chomp
    chomp -I
    | chomp

    文字列の最後にある改行を除去します。(LF, CRLF, CRのどれでも対応しています)

    substr 対象文字列 インデックス [カウント]
    substr -I インデックス [カウント]
    | substr インデックス [カウント]

    対象の文字列のインデックス目からカウント文字分（省略すると１文字）文字列を切り出します

    -c 入力をchompして（改行を除去して）から処理します
    -l 最後に改行文字を出力します
    -s 文字列のエンコードをSJISとして数えます。
    -e 文字列のエンコードをeucjpとして数えます。
    -w 文字列のエンコードをUTF8として数えます。

    eval コード

    コードを実行します。eval "print aaa; print bbb"などとしてください。eval print aaa; print bbbという風には実行できません

    fg ジョブ番号

    ジョブ番号のジョブをフォアグランドグループにします。（前面に持ってくる）
    bg ジョブ番号

    ジョブ番号のジョブにSIGCONTを送ります。バックグラウンドのまま実行を再開します。

    cpg ジョブ番号 プロセスグループID

    ジョブ番号のジョブのプロセスグループIDを変更します。(昔バグがあったころに必要だったコマンドです。いずれ廃止されます。)

    jobs

    ジョブの一覧を表示します。

    rehash

    コマンドライン補完のコマンド名の一覧のキャッシュを更新します。新しいプログラムがインストールされたら実行すると、そのプログラムの補完が有効になります。

    kanjicode [-s|-e|-w]

    -s kitutukiが処理する漢字コードをSJISとして設定します
    -e kitutukiが処理する漢字コードをEUCJPとして設定します
    -w kitutukiが処理する漢字コードをUTF8として設定します
    -q 実行結果を出力しません

    kitutukiが処理する漢字コードというのは具体的には変数や配列の添え字の数え方や文字の数え方に関連する内部関数の-s,-e,-wを指定しない場合のデフォルトの文字コードということです

    linefield [-Lw|-Lm|-Lu]

    -Lw kitutukiが処理する改行コードをCRLFとして処理します
    -Lm kitutukiが処理する改行コードをCRとして処理します
    -Lu kitutukiが処理する改行コードをLFとして処理します

    kitutukiが処理する改行コードというのは組み込みコマンドがパイプによって文字列を一行受け取る場合の改行コードです。ほかにもjoinやsplitなど組み込みコマンドの処理で使われます。

    var 変数名 変数名

    ローカル変数の宣言

    var 変数名=値 変数名=値...

    ローカル変数に値を代入したり初期化します

    var -I 変数名 変数名...
    | var 変数名 変数名...

    各変数名に入力から一行ずつ受け取って、chompして一行を各変数名に代入します

    > print aaa\nbbb | var a b; puts $a,$b
    aaa,bbb

    -ncオプションでchompせずに改行コードも一緒に変数に代入します

    -a パイプで受け取る文字列を一行単位ではなくて全て受け取る

    例 ls -al | var -a a

    aにls -alの出力が全て入ります

    -f 区切り文字(複数可能) 区切り文字列を改行でなく別のものに設定します。

    -nc 改行をchompせずに変数に代入する

    -I 入力から文字列を受け取り変数に設定する

    -p 出力を行う

    -l 改行を出力する

    -n 空文字を代入したらリターンコードの1を返す

    global 変数名 変数名

    グローバル変数の宣言

    global 変数名=値 変数名=値...

    グローバル変数に値を代入したり初期化します

    global -I 変数名 変数名
    | global 変数名 変数名...

    各変数名に入力から一行ずつ受け取って、chompして一行を各変数名に代入します
    -ncオプションでchompせずに改行コードも一緒に変数に代入します

    -a パイプで受け取る文字列を一行単位ではなくて全て受け取る

    例 ls -al | global -a a

    aにls -alの出力が全て入ります

    -f 区切り文字(複数可能) 区切り文字列を改行でなく別のものに設定します。

    -nc 改行をchompせずに変数に代入する

    -I 入力から文字列を受け取り変数に設定する

    -p 出力を行う

    -l 改行を出力する

    -n 空文字を代入したらリターンコードの1を返す

    export 変数名 変数名

    環境変数の宣言

    export 変数名=値 変数名=値...

    環境変数に値を代入したり初期化します

    export -I 変数名 変数名
    | export 変数名 変数名...

    各変数名に入力から一行ずつ受け取って、chompして一行を各変数名に代入します
    -ncオプションでchompせずに改行コードも一緒に変数に代入します

    -a パイプで受け取る文字列を一行単位ではなくて全て受け取る

    例 ls -al | export -a a

    aにls -alの出力が全て入ります

    -f 区切り文字(複数可能) 区切り文字列を改行でなく別のものに設定します。

    -nc 改行をchompせずに変数に代入する

    -I 入力から文字列を受け取り変数に設定する

    -p 出力を行う

    -l 改行を出力する

    -n 空文字を代入したらリターンコードの1を返す

    print 出力文字列
    print -I
    | print

    -l 文字列の最後に改行文字を追加する
    -I 入力を受け取って出力する
    -e エラー出力する

    puts 出力文字列
    puts -I
    | puts

    -nl 改行しない
    -I 入力を受け取って出力する
    -e エラー出力する

    compile ファイル名

    kitutukiのスクリプトをコンパイルしてコンパイル結果をファイル名.kioとして出力する

    load ファイル名

    kitutukiのスクリプトを読み込み実行する

    -kio コンパイル済みのスクリプトファイルを読み込み実行する
    -Lw スクリプトの改行コードはCRLFとして処理する
    -Lm スクリプトの改行コードはCRとして処理する
    -Lu スクリプトの改行コードはLFとして処理する
    -v 実行中のコマンド名を出力する

    exit

    kitutukiを終了する

    choise メッセージ 選択肢1 選択肢2 ....
    choise -I メッセージ
    | choise メッセージ

    -l 出力を改行する
    -v リストビュー
    -n 出力をインデックスにする
    -I 入力を受け取り一行文を一選択肢とする

    | split 区切り文字の正規表現

    入力を受け取り区切り文字で区切って区切りを改行として出力する

    -f 文字列  区切り文字を改行から文字列に変更するS
    -l 最後にフィールドを一つ追加して出力する

    >print aaa bbb ccc | split
    aaa
    bbb
    ccc

    >print aaa,bbb,ccc | split ,
    aaa
    bbb
    ccc

    >ls | chomp | split -f "," | puts
    main.c,test.c,Makefile,configure.in

    add 文字列

    パイプに文字列を追加する

    -n インデックス インデックス番目に文字列を追加する
    -l 行数 行数のところに文字列を追加する(0なら1行目)

    > print aaa | add ccc\n
    aaaccc

    > puts アンディ\nエフェメラ\nタッタ | add -l 1 $(puts アード)
    アンディ
    アード
    エフェメラ
    タッタ

    del
    del 文字数
    del -n インデックス 文字数

    文字数分パイプの文字列を末尾から削除する
    -n 末尾からではなくてインデックスから文字列を削除する

    >puts abcdE | del 
    abcd

    join 文字列

    改行区切りの文字列に文字列を間に挟んでつなげる

    -l 出力に改行コードをつける

    >ls kitutuki* | join , | del | add \n
    kitutuki,kitutuki.c,kitutuki.c.bak,kitutuki.h,kitutuki.kio,kitutuki.ksh,kitutuki.o

    | lines 行数|式 (ブロック) 行数|式...

    入力から受け取り指定された行数の行を表示
    ブロックがあれば、各行がグローバルパイプに入れられて
    ブロックが実行される
    先頭の文字が数字か*じゃなければ、式として扱われる

    -r 行を反転して表示する

    行数には

    数字 その行番号

    数字..数字 範囲指定

    \*数字1+数字2 y = 数字1*x + 数字2 [x=1,2,3...] のyにあたる行

    (lines \*2+1 --> 奇数の行)
    (lines \*2+0 --> 偶数の行)

    が使えます。

    >print aaa\nbbb\nccc | lines 0 0..1
    aaa
    aaa
    bbb

    >puts aaa\nbbb\nccc | lines -r
    ccc
    bbb
    aaa

    >print aaa\nbbb\nccc | lines 0 1..0
    aaa
    bbb
    aaa

    >print aaa\nbbb\nccc\nddd | lines 0 { | uc } 1 { | sub -g . D } 2..-1
    AAA
    DDD
    ccc
    ddd

    >print aaa\nbbb\nccc | lines ' | [ -re ^a ]'
    aaa

    | rows 数値 (ブロック)

    パイプで渡された先頭の一行の各文字ごとにブロックを実行するか
    ブロックが無ければ表示する

    > print abcdefg | rows 0 1 | puts
    ab

    > print abcdefg | rows 0 { | uc } 1..-1 | puts
    Abcdefg

    ary_new 配列名 要素1 要素2 .....
    配列を宣言

    ary_new -I
    | ary_new 配列名

    入力から各行を１要素として配列に代入(各行はchompされる)

    -a 入力を改行で区切らず全ての入力を一つの要素として代入
    -nc １行を１要素として配列に代入する場合chompしない
    -I 入力を受け取り配列の要素として代入する
    -p 出力する
    -l 出力に改行をつける

    ary_add -I 配列名
    | ary_add 配列名

    入力から各行を１要素として配列に追加する(末尾)

    | ary_add -n インデックス 配列名

    インデックスの場所に要素を追加する

    -a 入力を全て代入
    -n 数値 数値のところにインサート
    -nc chompしない
    -p 出力を行う
    -l 出力に改行をつける

    ary_erase 配列名 インデックス

    配列の要素を削除

    ary_clear 配列名

    配列名の配列を削除

    match

    正規表現の比較演算子

    match -I [正規表現] -> 標準入力を[正規表現]で比較
    コマンド | match [正規表現] -> コマンドの出力全てを[正規表現]で比較
    match [正規表現] [文字列] -> [文字列]を[正規表現]で比較
    コマンド | match -L [正規表現] -> grepの様に各行に正規表現がマッチするか比較する。
    コマンド | match -g [正規表現] -> コマンドの出力全てを[正規表現]で比較。マッチしても止まらず何度も比較を繰り返す
    match -g [正規表現] [文字列] -> [文字列]を[正規表現]で比較。マッチしても何度も比較を繰り返す

    -q 出力しない
    -nl 出力を改行しない
    -n 行番号をつける
    -I 標準入力から比較する文字列を受け取る。コマンドの２つ目からは自動的に-Iがあるとみなす
    -f [文字列] グループ化のマッチした文字列の出力の区切りに[文字列]を使う。デフォルトはタブ
    -g グローバル。一文字ずつ比較する
    -L 行指向比較。一行ずつ比較する。grepと似た動きをする。
    -i 正規表現が大文字と小文字を区別しない

    $PREMATCH マッチした前の部分
    $MATCH マッチした部分
    $POSTMATCH マッチした後の部分

    $MATCH_COUNT マッチした回数

    $数値 グループ化でマッチしたものが前から順番に入ります

    >ls | match -L -n minato_curses
    11:minato_curses.c
    12:minato_curses.o
    13:minato_curses.h

    (行指向)

    >ls | match .
    A

    (一文字ヒットしたら以後比較しない)

    >ls | match -g .
    A
    U
    T
    H
    O
    R
    S

    >print 'file:123' | match '(.+?):(\\d+)'
    file	123

    >print 'file:123' | match -f \n '(.+?):(\\d+)'
    file
    123

    >print abcdef | match '..(.).(.).'; puts $1,$2
    c	e
    c,e

    >puts mikan | match -f , '(m)i(kan)'
    m,kan

    matchはegrepと違ってマッチした行ではなくてマッチした部分を出力します。
    egrepのように行指向で処理を行いたい場合は-Lをつけてください。
    またグループ化した場合はそのグループ化してマッチした部分をフィールド区切りで出力します。(フィールドの初期値はタブ)

    >puts "file:123" | match '(.+?):(\\d+)'
    file	123

    matchは一行ごとに処理はしません。受け取った文字列全てに検索を行います。(デフォルトでは検索回数は１回だけ。-gで何回も)
    一行ごとに処理したい場合はwhile <> |などと組み合わせてください

    > ls | while <> | { | match -q -i m && | print }
    Makefile
    Makefile.in
    README.ja.txt
    autom4te.cache
    gmon.out.hayai
    gmon.out.osoi
    kitutuki_commands.c
    kitutuki_commands.o
    kitutuki_main.c
    kitutuki_main.o
    kitutuki_vm.c
    kitutuki_vm.o
    main.c

    ary_erase 配列名 インデックス

    配列の要素を削除

    ary_clear 配列名

    配列を削除する

    | sub 正規表現 変換後の文字列

    subは行指向の文字列の変換を行います。
    (文字列全体に対する変換はありません。)

    >ls
    kitutuki_vm.c
    kitutuki_vm.o
    libkitutuki.so
    libkitutuki.so.1
    libkitutuki.so.1.0
    main.c
    readline.c
    readline.o

    >ls | sub a A
    kitutuki_vm.c
    kitutuki_vm.o
    libkitutuki.so
    libkitutuki.so.1
    libkitutuki.so.1.0
    mAin.c
    reAdline.c
    reAdline.o

    グループ化した文字列は$1,$2,...に入ります。変換後の文字列で
    $1,$2を使うとグループ化した文字列が入ります。
    変換後の文字列に$自身を使いたい場合は$$を使ってください

    > print mikan | sub -l '(.)i(...)' '$1$2'
    mkan

    変換は一行に一回しか行いません。-gをつけると何回も行ってくれます。

    -g グローバル。一行に二度以上マッチしていたら、２度以上変換を行います
    -q 出力を表示しない
    -l 改行を出力する
    -i 大文字と小文字を区別しない

    マッチした回数はローカル変数SUBCOUNTに入ります。

    read ファイル名

    -a 全て読み込む

    一行読み込む。もう一度読み込むと続きから読み込む。全てを読み込むとリターンコード1を返す。途中まで読み込んでいるときはファイルがオープンになっているのでclose ファイル名しなくてはならない

    ファイル名にはSTDINが使うとコンテキストに応じた入力から一行読み込む

    close ファイル名

    ファイルを閉じる

    -q メッセージを出力しない

    cd ディレクトリ

    ディレクトリに移動する。$PWDに現在のカレントディレクトリが入る

    cdだけだと$HOMEに移動する

    | selector

    入力から読み込んだテキストからユーザーが一行または複数行選択して選択した行を出力する

    -l 改行を出力する
    -r 前回のカーソル位置、スクロールトップ位置を使用する。
    -c カーソル位置 カーソル位置を設定する
    -t スクロールトップ位置 スクロールトップ位置を設定する
    -m 複数行の選択を許す。スペースで複数行選択できる
    -s 入力テキストをSJISとして処理する
    -e 入力テキストをEUCJPとして処理する
    -w 入力テキストをUTF8として処理する

    max 数値1 数値2

    大きいほうの数値を出力する

    -l 改行を出力する

    min 数値1 数値2

    小さいほうの数値を出力する

    -l 改行を出力する

    extname ファイル名

    -I 入力をパイプや標準入力から受け付ける
    -l 改行を出力する

    拡張子を出力する

    parentname ファイル名

    -I 入力をパイプや標準入力から受け付ける
    -l 改行を出力する

    ディレクトリ名のみ出力する

    noextname ファイル名

    -I 入力をパイプや標準入力から受け付ける
    -l 改行を出力する

    拡張子以外のファイル名を出力する
    raise メッセージ

    メッセージをエラーメッセージに入れて実行を停止する

    hash_new 連想配列名 連想配列名2 ...

    連想配列を作成する

    | hash_new 連想配列名

    パイプ入力から連想配列のデータを受け取って連想配列を作る

    > print key value key2 value2 key3 value3 | split | hash_new a
    > puts $a[key]
    value

    hash_new -I 連想配列名

    標準入力から連想配列のデータを受け取って連想配列を作る

    | hash_add 連想配列名

    > print key value | split | hash_add a

    連想配列にデータを追加する

    hash_erase 連想配列名 キー

    連想配列の要素を削除する

    hash_clear 連想配列名 連想配列名2 ...

    連想配列を削除する

    printf フォーマット 引数1 引数2 ...
    | printf フォーマット

    -c エラーチェック。引数の数が少なかったらエラーにする。

    C言語やPerlやその他の言語でよくあるフォーマット指定の文字列出力です。
    詳しくはそちらで学んでください。
    フォーマット指定には(今のところ)%sによる文字列指定しか使えません。
    %sフォーマットにはオプションがつけられます

    %(-)数字1(数字2...数字3)s
    
    数字1は幅。数字2,数字3はkitutukiの変数の添え字と同じような引数文字列の範囲指定です。
    数字1の幅はマイナスで左詰。何もつけないと右詰です。注意して欲しいのはCのprintfと違ってこの数値より引数の文字列数が大きいと、この数値の数の文で引数の文字列が切り捨てられます。
    パイプによる引数の受け取りは一行を一引数と考えて処理されます。

    > printf "%s%10s,%-10(1..2)s,%1s\n" abcdefg abcdefg abcdefg abcdefg
    abcdefg,   abcdefg,bc        ,a

    > ls | printf "[%s], [%s], [%s]\n[%s], [%s], [%s]\n"
    [AUTHORS], [kitutuki_commands.c], [kitutuki_curses.c]
    [kitutuki_debug.c], [kitutuki_extra.c], [kitutuki_hash.c]

    sort ブロック

    一行ごとのソート。

    $a 左の一行が入っている
    $b 右の一行が入っている

    > ls | sort { [ $a -slt $b ] }

    lsの出力を行ごとに昇順に並び替える

    > ls | sort { [ $a -sgt %b ] }

    lsの出力を行ごとに降順に並び替える

    > print $ARGV | sort { [ $a -slt $b ] } | ary_new ARGV2

    ARGVを昇順にソートしてARGV2に代入

    ++ ローカル変数名

    ローカル変数の値を１増やす

    | ++
    パイプから入力を受け取り1増やして出力する

    -- ローカル変数名
    ローカル変数の値を１減らす

    | --
    パイプから入力を受け取り1減らして出力する

    | + 数字
    パイプから入力を受け取り数字で加算して出力する

    | - 数字
    パイプから入力を受け取り数字で減算して出力する

    | \* 数字
    パイプから入力を受け取り数字で乗算して出力する

    | / 数字
    パイプから入力を受け取り数字で除算して出力する

    | mod 数字
    パイプから入力を受け取り数字で除算して、余りを出力する

    | pow 数字
    パイプから入力を受け取り数字乗して出力する

    range 数字１ 数字２

    数字１から数字２までの範囲の数字を作成

    > range 1 5
    1
    2
    3
    4
    5

    --> seqを使ってください。ただseqをループの中で使うのはかなりパフォーマンスが悪いです。気が向けばrangeをseqクローンに改造します。

    こんなことにも使えます

    > ls | ary_new a; puts $a[$(range 30 50 | chomp | join ,)]

kitutuki.kshで定義されているユーザー関数

    foreach 文字列...文字列 ブロック

    並べられた文字列毎にブロックを実行する。文字列はグローバルパイプに入っている。

    >foreach a b c { | print }
    a
    b
    c

    >ary_new A a b c
    >foreach $A( ) { | print }
    a
    b
    c

    for 変数 in 文字列 文字列.... ブロック

    並べられた文字列をひとつずつ変数に入れてブロックを実行する。

    >for a in a b c { puts $a }
    a
    b
    c

    >ary_new A a b c
    >for a in $A( ) { puts $a }
    a
    b
    c

    case 値 正規表現 ブロック 正規表現 ブロック...

    値が正規表現にマッチするなら次のブロックを実行します。

サンプルスクリプト

    paste -d , a bと同じことをする

    while <a> | var -n a || <b> | var -n b { puts $a,$b }

    lsの出力の各ファイル名を一文字目だけ大文字にして出力

    ls | while <>| { | var a; uc $a[0]; puts $a[1..-1] }
    ls | while <>| { | rows 0 { | uc } 1..-1 | puts } | chomp

    lsの出力でminatoにマッチして~にマッチしないファイル名とその数を出力
    (grep minato | grep -v \~ | wc -lみたいなもの)

    nr=1; count=0; ls | while <> | { | match -q minato && | ! match -q \~ && (++ count; print $nr\:; | print ); ++ nr }; puts $count

    egrep

    ls | while <> | { | [ -re 正規表現 ] && | print }

パフォーマンス

    基本的にperl, python, rubyやsed, awkよりもパフォーマンスが悪いです。2倍から5倍くらい遅くなります。ただ、ワンライナーはこれらのコマンドを組み合わせるより書きやすいし十分な速度が出ていると思います。(４Mのテキスト処理でもそれなりの速度が出ます）
