saphire shell

1.0.1 redirect

    > file name              write STDOUT to a file
    %2> file name            write STDERR to a file
    >> file name             append STDOUT to a file
    %2>> file name           append STDERR to a file
    < file name              using file as STDIN
    %>                       change stdout and stderr to stdout

    
1.0.2 statment

    ;               normal statment separator
    \n              normal statment separator
    &&              If a statment return code is 0, saphire evals next commandline.
    ||              If a statment return code is not 0, saphire evals next commandline.
    &               run with bachground

1.0.3 background
    You can't use saphire inner commands and user commands as background.

    > def fun { sleep 10 }
    > fun &

    the above can't run fun as background

    > def fun { sleep 10 & }
    > fun

    the above can run sleep as background

    run "jobs" to see background jobs and suspended jobs which is caught Cntrol-Z.

    fg [job number]

    above can make the job forground

    bg [job number]

    above can send SIGCONT signal to the job.

1.1 quote

    Saphire has single quote and double quote, normal quote.

    > puts first string; puts second string
    first string
    second string

    > puts first string \; puts second string
    first string ; puts second string

    If you want to send \ to programs, use \\

    > puts This is \\
    This is \\

    > puts 'first string; puts second string'
    first string; puts second string

    > puts "first string; puts second string"
    first string; puts second string

    different between single quote and double quote

    > futatuna=red
    > puts "share $futatuna aznable"
    shar red aznable

    > futatuna=red
    > puts 'share $futatuna aznable'
    share $futatuna aznable

    One quote disipates the nature in other quote

    > puts "\aaa"
    \aaa

    > puts '\aaa'
    \aaa

    > puts "'"
    '

    > puts '"'
    "

    > puts \"
    "

    > puts \'
    '

1.2 special string

    saphire can include line fields and tab.

    \n linefield
    \r carrige return
    \t tab

    > print Nice to meet you\nHello
    Nice to meet you
    Hello

    > print Nice to meet you\\nHello
    Nice to meet you\nHello\n

1.2.5 null string
    
    ''
    ""

    > puts "aaaa.c" | sub '\.' ''
    aaaac

1.3 arithmistic expression

    same bash. You can use $((expression))

    saphrie uses float.

    the below expression saphire has

    pai               == 3.14
    log 
    int 
    ceil 
    floor 
    log10 
    abs 
    exp 
    sin 
    cos 
    asin 
    acos 
    atan 
    sinh 
    cosh 
    sqrt 

    exp ** exp --> pow
    exp * exp
    exp / exp
    exp % exp  --> mod
    exp + exp
    exp - exp

    the above is the order of priority level.

    > puts $((1.1 + 1))
    2.1

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

    > puts $(( log 3 + 3 ))    # equals log 6
    1.791759

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

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

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

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

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

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

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

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

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

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

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

    >a="a;b"; puts "$a"
    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     # キーをとる
    あ

    高速な変数参照方法として@変数名があります。これは一単語として変数を展開します。つまり

    >a=abc; puts aaa@a

    とするとputsにはaaaとabc二つの引数が渡されます。

    >a=abc; puts aaa abc

    と同じです。

    >a=abc; puts aaa$a

    とするとputsにはaaaabcのひとつの引数が渡されれるのと違います。

    巨大なテキストファイルを処理するときのループでは@を使うことをお勧めします。

    > cat big_file.txt | while(<>|){ | split | ary_new a; puts @a[0] @a[1] }

    あと、巨大なテキストファイルをコマンドラインに貼り付ける時にも@を使うことをお勧めします。たとえばbig_file.txtが数Mバイトのサイズがあるとき

    > cat big_file.txt | var -a a; puts $a

    はputs (big_file.txtの内容)と展開してから、その巨大なコマンドラインを字句解析して固まってしまいますが

    > cat big_file.txt | var -a a; puts @a

    は@aを変数aとして字句解析してからvmの内部でaを展開してputsに直接わたします。結果、固まりません。

    変数の展開は基本的に@による展開をお勧めします。$が必要な場面というのはあまりないはずです。

    > a=abc; puts abc${a}abc

    も

    > a=abc; printf "abc%sabc" @a

    と書けます。

    @の仲間に@@変数名があります。
    これは配列の場合ひとつの要素が一つの引数として渡されます。

    > ls | ary_new a; cp @a /tmp
    aの内容がすべて一つの引数として展開されエラー

    > ls | ary_new a; cp @@a /tmp
    aの内容一つずつ一つの引数として展開されるのでok

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

    @()による高速なコマンド展開もあります。

    >puts @(print a: print b)
    ab

    ただ、こちらは結果はコマンドの引数でひとつとして扱われます。

    >puts @(print a)@(print b)
    a b

    $()はコマンド結果をコマンドラインに貼り付けて字句解析されてから実行されますが、@()は実行時にvmで実行されて直接putsに渡されます

    例えばbig_fileが数Mバイトのテキストファイルなら

    > puts $(cat big_file.txt)

    はbig_file.txtiがコマンドラインに貼り付けられて字句解析してからputs が実行されるので膨大な量のコマンドラインを字句解析するので固まりますが

    > puts @(cat big_file.txt)

    は固まりません。

    もう一つあります。@@()です。
    これはコマンドの出力結果が一行ごとに一つの引数として扱われます

    > cp @(ls) /tmp
    lsの内容がすべて一つの引数として展開されエラー

    > cp @@(ls) /tmp
    lsの内容一つずつ一つの引数として展開されるのでok

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 libsaphire.so.1.0 saphire saphire.c saphire.c.bak saphire.c.bak2 saphire.h saphire.o saphire.sao saphire.sh test.c tmp todo.txt usage.en.txt usage.ja.txt

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

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

1.7 グローバルパイプ

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

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

    グローバルパイプはスタッカブルです。
    何度もグローバルパイプに書き込むとスタックに積まれます。

    > echo aaa |
    > echo bbb |
    > | print
    bbb
    > | print
    aaa

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

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

    (ただしaaaがスタックに積まれたまま）

    逆に|>でスタックに積まれず最後のスタックに上書きされます

    > echo aaa |
    > echo bbb |>
    > | print
    bbb
    > | print
    エラー

    注意してほしいのはグローバルパイプへの出力はコマンドのリターンコードが0のときしか行われないことです

    > ls | while(<>|) { | print }
    main.c
    common.h

    > | print
    エラー

1.7 ファイルハンドル
    
    <>
    はreadと同義です

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

1.7 ブロック

    ブロックには中括弧によるブロックがあります。

    > def fun { print エフェメラ; puts クルツ }

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

    > puts { aaa }
    { aaa }
    
    >{ puts aaa }
    aaa

    一つ目の引数ならサブシェルと同じ

1.7 サブシェル

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

    > (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 | uc
    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; (puts $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 saphire.sh
    def each(_ARGV) {
        NR=1
        while(<> |) {
            eval $_ARGV[0]

            NR=$(($NR + 1))
        }
    }

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

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

   関数はreturnで戻れます

1.9 ブレース展開
    
    今のところありません。

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される

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

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

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

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

    と補完されます

    実装はreadline.cで行っています。ただ、分からないことが多くかなり適当な実装です。また整理します。
    
2.0.6 オプション
    -c "コマンド" コマンドの実行
    -rs ランタイムスクリプトをソースファイルとして実行
    -ro ランタイムスクリプトをコンパイル済みファイルとして実行(デフォルト)
    -rn ランタイムスクリプトを実行しない
    -ts ターミナル漢字コードをsjisとする
    -tw ターミナル漢字コードをutf8とする
    -te ターミナル漢字コードをeucjpとする
    -s saphireの漢字コードをsjisとする
    -w saphireの漢字コードをutf8とする
    -e saphireの漢字コードをeucjpとする
    -Lu ラインフィールドの設定をLFとする
    -Lm ラインフィールドの設定をCRとする
    -Lw ラインフィールドの設定をCRLFとする
    --version バージョンを表示する
    
2.0.7 スクリプトファイルの実行

    saphire スクリプトファイル名
    sash スクリプトファイル名
    
    とすることでスクリプトファイルを実行できます。
    (saphireの方がインタラクティブシェル関連のライブラリをロードしない分速い)
    
    グローバル変数$SCRIPT_FILE_NAMEにスクリプトファイル名が入っています。
    配列$ARGVに引数が入っています。
    
    スクリプトファイルはUTF8で書かれている必要があります。
    また改行コードはLFで無いといけません。
    
2.1 組み込みコマンド

    msleep

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

    true

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

    false

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

    [

    -I 入力を受け取って比較

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

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

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

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

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

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

    -reの仲間に下があります

    -rei 大文字小文字区別しない
    -rem 複数行対応
    -reim, -remi 大文字小文字区別しない、複数行対応

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

    -slt

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

    -sle

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

    -sgt

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

    -sge

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

    -silt

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

    -sile

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

    -sigt

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

    -sige

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

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

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

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

    >print aaa | if([ -I = aaa ]) { puts yes } else { no }
    yes

    この条件式は特別な構文ではなく組み込みコマンドなので書き方に注意してください
    [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 判定だけして出力をしない。リターンコードの値だけを見たいときに使います。

    -nl 出力の最後に改行を加えない

    -I 標準入力から文字列を受け取る(saphireの組み込みコマンドではパイプの二番目以降にコマンドが実行されると自動的に-Iがついたものとして処理されます)
    -c 数値 検索回数の指定。2が指定されると２回目のマッチした位置が返されます

    -n 数値 文字列の検索開始位置を指定します。
    -b バイト単位で数えたインデックスを返す
    -t ターミナルでの文字列幅で数えたインデックスを返す。

    length 文字列
    length -I
    | length 

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

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

    uc
    uc -I
    | uc

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

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

    lc
    lc -I
    | lc

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

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

    chomp
    chomp -I
    | chomp

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

    pomch
    | pomch

    文字列の最後に改行が無ければ改行を追加する(LF, CRLF, CRの区別はlinefieldの設定による)

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

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

    eval コード
    | eval

    コードを実行します。eval "print aaa; print bbb"などとしてください。eval print aaa; print bbbという風には実行できません
    | evalはパイプからデータを読み込んでそれを実行します

    fg ジョブ番号

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

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

    jobs

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

    rehash

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

    kanjicode [-s|-e|-w]

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

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

    linefield [-Lw|-Lm|-Lu]

    -Lw saphireが処理する改行コードをCRLFとして処理します
    -Lm saphireが処理する改行コードをCRとして処理します
    -Lu saphireが処理する改行コードをLFとして処理します
    -q 実行結果を出力しません

    saphireが処理する改行コードというのは組み込みコマンドがパイプによって文字列を一行受け取る場合の改行コードです。ほかにも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 改行を出力する

    global 変数名 変数名

    グローバル変数の宣言

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

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

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

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

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

    例 ls -al | global -a a

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

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

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

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

    -p 出力を行う

    -l 改行を出力する

    export 変数名 変数名

    環境変数の宣言

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

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

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

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

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

    例 ls -al | export -a a

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

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

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

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

    -p 出力を行う

    -l 改行を出力する

    print 出力文字列
    print -I
    | print

    -l 文字列の最後に改行文字を追加する
    -I 入力を受け取って出力する
    -f 区切り文字を設定する

    puts 出力文字列
    puts -I
    | puts

    -nl 改行しない
    -I 入力を受け取って出力する
    -f 区切り文字を設定する

    compile ファイル名

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

    load ファイル名

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

    -sao コンパイル済みのスクリプトファイルを読み込み実行する

    exit

    saphireを終了する

    split -I [区切り文字の正規表現]

    split 文字列 [区切り文字の正規表現]

    文字列を区切り文字の正規表現で区切って区切りを改行として出力する
    区切り文字が省略されると\s+が渡される

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

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

    -f 文字列  区切り文字を改行から文字列に変更するS
    -I 標準入力から入力を得る
    -m 複数行対応
    -i 大文字小文字無視

    >split "aaa bbb ccc"
    aaa
    bbb
    ccc

    >split "aaa,bbb,ccc" ,
    aaa
    bbb
    ccc

    >print aaa bbb ccc | split
    aaa
    bbb
    ccc

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

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

    add 文字列

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

    -n インデックス インデックス番目に文字列を追加する
    -l 行数 行数のところに文字列を追加する(0なら1行目)
    -s 文字列のエンコードをSJISとして数えます。
    -e 文字列のエンコードをeucjpとして数えます。
    -w 文字列のエンコードをUTF8として数えます。
    -b -nのインデックスをバイトi単位で数えます

    > print aaa | add ccc\n
    aaaccc

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

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

    文字数分パイプの文字列を末尾から削除する

    -n 末尾からではなくてインデックスから文字列を削除する
    -s 文字列のエンコードをSJISとして数えます。
    -e 文字列のエンコードをeucjpとして数えます。
    -w 文字列のエンコードをUTF8として数えます。
    -b -nのインデックスをバイトi単位で数えます

    >print abcdE | del 
    abcd

    join -I [区切り文字列]
    join 文字列 [区切り文字列]
    | join [区切り文字列]

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

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

    x 文字列 個数

    文字列を個数分出力します

    | x 個数

    入力文字列を個数分出力します

    > print a | x 5 | pomch
    aaaaa

    | 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 -I } 1 { sub -I -g . D } 2..-1
    AAA
    DDD
    ccc
    ddd

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

    >ls | lines 0 { var -I a } 1..-1 { var -a b }

    aに１行目が
    bに最後の行が入ります

    >ls | lines 0 { var -I a } 1...-1 { var -a b }

    aに１行目が
    bに２行目以降が入ります

    | rows 数値 (ブロック)

    -b バイト単位でインデックスを指定します
    -s 文字列のエンコードをSJISとして扱いインデックスを数えます。
    -e 文字列のエンコードをeucjpとして扱いインデックスを数えます。
    -w 文字列のエンコードをUTF8として扱いインデックスを数えます。
    -r 文字列を逆順にして出力する

    数字 その文字インデックス
    数字..数字 文字インデックス範囲
    数字...数字 文字インデックス範囲

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

    数字..数字は 指定された文字はグローバルパイプに入られてブロックは一度だけ実行されます
    数字...数字は、指定された文字全てで一度ブロックが実行されます

    > print abcdefg | rows 0 3 { puts -I }
    ad

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

    > print abcdefg | rows 0 { uc -I } 1...-1 { puts -I }
    Ab
    c
    d
    e
    f
    g

    > print abcdefg | rows -r |pomch
    gfedcba

    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 正規表現が大文字と小文字を区別しない
    -m 複数行マッチする
    -r 同じ文字列であればマッチした位置を記憶し二回目以降その記憶した位置以降から検索する(posコマンドで位置表示、操作できる）

    $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
    saphire_commands.c
    saphire_commands.o
    saphire_main.c
    saphire_main.o
    saphire_vm.c
    saphire_vm.o
    main.c

    > while(puts AUTHORS | match -r .) {}
    A
    U
    T
    H
    O
    S

    scan 文字列 正規表現
    | scan 正規表現

    内部的にはmatch -g と同じです。

    [文字列]を[正規表現]で比較。マッチしても何度も比較を繰り返す
    -I 標準入力から比較する文字列を受け取る。コマンドの２つ目からは自動的に-Iがあるとみなす
    -f [文字列] グループ化のマッチした文字列の出力の区切りに[文字列]を使う。デフォルトはタブ
    -i 正規表現が大文字と小文字を区別しない
    -m 複数行マッチする

    $MATCH_COUNT マッチした回数

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

    >ls | scan .
    A
    U
    T
    H
    O
    R
    S

    pos 文字列
    | pos
    pos 文字列 数値
    | pos 数値

    match -r の現在の検索位置を表示、設定する

    > match -r abcde .
    a
    > pos abcde
    1
    > pos abcde 2
    > match -r abcde .
    c

    erase 文字列 正規表現
    | erase 正規表現

    正規表現にマッチする部分を文字列から削除する
    グループ化した場合はマッチする部分のうちグループ化された部分だけ文字列を削除する

    -g 何度も正規表現を繰り返す
    -m 複数行対応
    -i 大文字小文字無視

    > erase abcdefg\n .
    bcdefg

    > erase abcdefg\n '.$'
    abcdef

    > puts abcdefg | erase '(.).(.)$'
    abcdf

    ary_erase 配列名 インデックス

    配列の要素を削除

    ary_clear 配列名

    配列を削除する

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

    subは全体の変換を行います。
    (行志向の変換が行いたければwhile(<>|)と組み合わせてください)

    >ls
    saphire_vm.c
    saphire_vm.o
    libsaphire.so
    libsaphire.so.1
    libsaphire.so.1.0
    main.c
    readline.c
    readline.o

    >ls | sub a A
    sAphire_vm.c
    saphire_vm.o
    libsaphire.so
    libsaphire.so.1
    libsaphire.so.1.0
    main.c
    readline.c
    readline.o

    >ls | while(<> |){ | sub a A }
    sAphire_vm.c
    sAphire_vm.o
    libsAphire.so
    libsAphire.so.1
    libsAphire.so.1.0
    mAin.c
    reAdline.c
    reAdline.o

    > sub -g aaa a x | pomch
    xxx

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

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

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

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

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

    read

    -a すべてを読み込む
    -n 数字 数字行読み込む

    readはコンテキストに応じた標準入力から一行読み込む。もう一度読み込むと続きから読み込む。全てを読み込むとリターンコード1を返す。

    > puts aaa\nbbbb | read
    aaa

    > puts aaa\nbbb | (read | chomp | add D\n; read | chomp | add E\n)
    aaaD
    bbbE

    > puts aaa\nbbb | read -a
    aaa
    bbb

    > puts aaa\nbbb\nccc\neee\nfff | (read; read -n 2 | cat -n; read -a)
    aaa
          1 bbb
          2 ccc
    eee
    fff

    read ファイル名

    ファイル名のファイルから一行読み込む。
    EOFに達すると1を返しcloseされる。
    ファイルはsaphireの終了時にcloseされる

    -n 数値 数値行読み込む
    -a 全て読み込む

    cd ディレクトリ

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

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

    | selector

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

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

    連想配列を削除する

    sweep

    全てのグローバル変数、連想配列、配列、グローバルパイプを消す

    -f 関数定義もクリアする

    show

    全てのグローバル変数、連想配列、配列の定義情報を見る

    -v グローバル変数の値も表示する

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

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

    フォーマット指定には(今のところ)%sによる文字列指定しか使えません。
    %sフォーマットにはオプションがつけられます

    %(-)数字1(数字2..数字3)s
    
    数字1は幅。数字2,数字3はsaphireの変数の添え字と同じような引数文字列の範囲指定です。
    数字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], [saphire_commands.c], [saphire_curses.c]
    [saphire_debug.c], [saphire_extra.c], [saphire_hash.c]

    | sort
    | sort ブロック

    一行ごとのソート。

    ブロックを省略すると { [ $a -slt $b ] }が渡される

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

    > ls | sort
    > 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 ,)]

    pcat ブロック ブロック ....

    複数のブロックの出力結果をパイプに流す

    > pcat { echo aaa } { puts bbb } | less
    aaa
    bbb

    | ptee ブロック ブロック ....

    パイプで受け取った入力を複数のブロックに渡す
    受け取ったデータは標準入力に入っています

    >puts aaa | ptee { cat } { print -I } { sub -I -g a b }
    aaa
    aaa
    bbb

    ネストも可能

    > puts aaa | ptee { cat } { ptee -I { print -I } { print -I } } | less
    aaa
    aaa
    aaa

    return 数値

    第一引数を終了コードとして関数から復帰する
    数値を省略すると終了コードが0となる

saphire.saで定義されているユーザー関数

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

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

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

    >ary_new A a b c
    >foreach $A< > { | 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

    >ary_new A a b c
    >for a in @@A { puts @a }
    a
    b
    c

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

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

サンプルスクリプト

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

    ls | while(<>|) { | var a; uc $a[0]; puts @a[1..-1] }
    ls | while(<>|) { | rows 0 { uc -I } 1..-1 { pomch -I } | 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よりもパフォーマンスが悪いです。4倍から15倍くらい遅くなります。巨大なテキストファイルの処理にはある程度のマシンパワーが必要です。4Mバイトサイズの簡単なテキスト処理(フィールドの切り出し結果をファイルに保存)でCoreDuo2 3.0Gクラスで1.6秒(rubyで0.4秒)、AtomのモバイルPCで8秒程度(rubyで2秒)になります。
    ソートはもっと遅いです。2万行のソートの第2フィールドによるソートして結果をファイルに出力する場合、CoreDuo2 1.6Gクラスで1分、Atomで3分程度かかります。ちなみにperlだと数秒です。（確か16倍くらいのパフォーマンスの差がありました）saphire シェルはインタラクティブにテキスト処理ができるのをうりとしていますので、身の回りの簡単なテキスト処理で使っていただけると有難いです。

