シェルスクリプト言語xyzshのソースコード。
Rev. | e6937031845487331f4725d07d3efa18e748b1ad |
---|---|
大小 | 28,934 字节 |
时间 | 2013-02-04 18:12:09 |
作者 | ab25cq |
Log Message | 1.3.4
|
0.0 序文
ようこそ、xyzshの世界へ。
xyzshはインタラクティブシェルでもありテキスト処理ツールでもあるスクリプト言語です。
PerlやRubyのようなテキスト処理コマンドを含み、オブジェクト指向プログラミングも行えます。
もちろん、インタラクティブシェルではコマンド名やオブジェクト名のコマンドライン補完も行われます。
何度も使わないような書き捨ての処理を短時間で、(時には対話的に)書く用途に向いていると思います。
対象とするユーザーとしては、bashやperl, python, ruby, sed, awkなど一通りマスターしたけれども
「もっと何か無いかな?」 と思っておられる方です。
初心者の方は最初にbashやperlなどを学んだほうがいいと思います。
(大抵のシステムではそちらがインストールされているので)
使用に当たっては、このプログラムによって被る結果には自分で責任を持つことをお願いします。
このプログラムのラインセンスはMITラインセンスです。自由に再配布でき書き換えることもできます。
詳しくはwikipediaなどでMITラインセンスについて調べてください。ここでは述べません。
ネット上の配布元としては
安定板 http://sourceforge.jp/projects/xyzsh
開発版 http://sourceforge.jp/projects/xyzsh/scm/git/xyzsh/
にあります。
gitを使えば、あなたもxyzshの開発者となることができます。
コマンドの追加やバグの修正など、改良結果のコミットは歓迎させていただきます。
forkして新規プロジェクトとして開発を行っても全然構いません。
1.0 構文
(オブジェクト::)*コマンド (引数|-オプション|ブロック) (引数|-オプション|ブロック) ...
(例)
> ls
> ls -al
> ls | sub -global a (|uc)
> sys::dmesg | sub -global . | less
コマンド名や引数には英数字以外にも記号も使うことができます。
xyzshで使われる特殊文字を使いたい場合は次のクォートを使ってください。
1.1 クォート
xyzshで使われる特殊な文字を普通の文字列として使いたい場合、クォートする必要があります。
クォートの方法は
1. シングルクォートで囲む (例) 'string'
2. ダブルクォートで囲む (例) "string"
3. 特殊文字の前にクォートを付ける (例) \;
の三つがあります。
クォートとクォートの関係は排他的で、あるクォートの中では他のクォートは意味を失います。
> print "'string'"
'string'
> print '"string"'
"string"
> print '\\'
\\
ただしダブルクォートの中ではクォートは意味を持ちます。
> print "\""
"
> print "\\"
\
シングルクォートの中ではクォートは意味を持ちません。
> print '\''
error
> print '\\'
\\
クォートの後に特定の英字を置くことでコントロールキャラクタがコマンドラインに埋め込まれます。
\n 改行
\r キャリッジリターン
\a ベル
> print "Hello World\nHello World\nHello World\n"
Hello World
Hello World
Hello World
これはシングルクォート内では展開されません。
> print 'Hello World\nHello World\nHello World\n'
Hello World\nHello World\nHello World\n
1.2 パイプとコンテキストパイプ
コマンドはパイプで繋げることができます。その場合、前のコマンドの出力が次のコマンドの入力となります。
> ls | sub -global . X
ls(カレントディレクトリのファイルの一覧を出力する) のファイル名のリストの出力がsub -global . X(全ての文字をXに変換する)の入力となり実行されます。
> ls | less
lsの出力がless(画面を制御してlsの出力を見やすいようにする)の入力となり実行される。
> ls | ( | less; | sub -global . X )
()はサブシェルで複数の文を一つのコマンドとして扱えます。| lessはコンテキストパイプを使って入力を受け取っています。
コンテキストパイプはコマンドによってブロック内で受け取れる入力が変わるパイプです。
この場合(subshellの場合)はブロック内で前のコマンドの出力を受け取ることができます。
よってlsの出力をlessが受け取って実行し、次にlsの出力がsub -global . Xで受け取られて標準出力に出力されます。
1.3 文の区切りと戻り値
; \n 普通の区切り
|| 前の文の戻り値が偽なら次の文を実行する
&& 前の文の戻り値が真なら次の文を実行する
& 最後のコマンドが外部コマンドであれば、最後の外部コマンドをバックグラウンドで実行する
> pwd; pwd
/home/ab25cq
/home/ab25cq
> make && make install
makeを実行して戻り値が真なら(makeが成功すれば)make installを実行する
> make || raise "make error"
makeを実行して戻り値が偽なら(makeが失敗すれば)エラーを起こす
文の最初に!を付けることによって戻り値の真偽を反転することができます。
> ! true
return code is 1
> ! false
1.4 オプション
-から始まる引数は全てオプションとして扱われます。
-から始まる引数をオプションとして扱いたくない場合はクォートしてください。
> sub -global . X
-globalはsubにオプションとして渡される
> print "-aaa" | less
-aaaはオプションとして扱われずにlessに渡せる
> echo -aaaa | less
など外部コマンドに-から始まる文字列を渡す場合はクォートされてもされなくてもechoに文字列として渡されます。
1.5 基本的なコマンド
if 内部コマンドは条件分岐を行います。
if (一つ目の条件式) (一つ目の条件式が真なら実行されるブロック) (二つ目の条件式) (二つ目の条件式が真なら実行されるブロック),..., (全ての条件式が実行されなければ実行されるブロック)
ifは単にブロックを見ているだけなのでelseやelifなどは好きに入れていただいても構いません。
> var a | if(|= main\n) (print true\n) else (print false\n)
変数aの出力がmain\nならtrueを表示、違うならfalseを表示。
条件式内部コマンドには以下があります。
-n 入力があるなら真
-z 入力がないなら真
-b ファイルが存在し、ブロックスペシャルファイルなら真
-c ファイルが存在し、キャラクタスペシャルファイルなら真
-d ファイルが存在し、ディレクトリなら真
-f ファイルが存在し、通常ファイルなら真
-h
-L ファイルが存在し、シンボリックファイルなら真
-p ファイルが存在し、名前付きパイプであれば真
-t
-S ファイルが存在し、ソケットであれば真
-g ファイルが存在し、set GIDされていれば真
-k ファイルが存在し、stickyビットが立っていれば真
-u ファイルが存在し、set SUIDされていれば真
-r ファイルが存在し、読み取り可能であれば真
-w ファイルが存在し、書き込み可能であれば真
-x ファイルが存在し、実行可能であれば真
-O ファイルが存在し、実効ユーザーIDによる所有であれば真
-G ファイルが存在し、実効グループIDによる所有であれば真
-e ファイルが存在すれば真
-s ファイルが存在し、サイズが0より大きければ真
= 入力が引数と等しければ真
!= 入力が引数と等しくなければ真
-slt 入力が引数より文字列として小さければ真
-sgt 入力が引数より文字列として大きければ真
-sle 入力が引数より文字列として小さいか同じであれば真
-sge 入力が引数より文字列として大きいか同じであれば真
-eq 入力が引数より数値として同じであれば真
-ne 入力が引数より数値として同じでなければ真
-lt 入力が引数より数値として小さければ真
-le 入力が引数より数値として小さいか同じであれば真
-gt 入力が引数より数値として大きければ真
-ge 入力が引数より数値として大きいか同じであれば真
-nt ファイルが引数のファイルより新しければ真
-ot ファイルが引数のファイルより古ければ真
-efA ファイルが引数のファイルと同じi-nodeであれば真
=~ 入力が引数の正規表現とマッチすれば真(ローカル変数のPREMATCHにマッチの前の部分、 MATCHにマッチした部分、POSTMATCHにマッチした後の部分, 1など数値の番号にグループ化にマッチした文字列が入ります)
ifもパイプを繋げることも可能です。
> var a | if(|= main\n) (print true\n) else (print false\n) | scan . | less
出力されたtrue\nもしくはfalse\nを毎行一文字に変換してからlessで表示。
while内部コマンドは条件式が真の間ループを続けます。
> print 0 | var a; while(var a | -le 5) ( print $a; ++ a)
012345
これもパイプを繋げることができます。
> print 0 | var a; while(var a | -le 5) (print $a; ++ a ) | pomch
Control-cが押されるとsignal interruptエラーが起きて実行を停めます。このエラーはtry内部コマンドで受け取ることができます。
1.6 変数
xyzshの変数には以下の3つがあります。
1. 文字列、もしくは数値
文字列1行分を格納します。文字列は数値としても扱われます。
> ls | var a b c
lsの最初から3行目までが1行ずつa, b ,c に格納されます。
変数を出力したい場合は
> var a b c
lsの1行目
lsの2行目
lsの3行目
と出力することができます。この出力には改行を含みます。
変数を参照したい場合は
> print "$a $b $c" \n
lsの1行目 lsの2行目 lsの3行目
となります。この場合は改行を含みません。
2. 動的配列
全文を格納したい場合は
> ls | ary a
とします。lsの出力全てがaに格納されます。一つの行が一つの要素として格納されます。
出力したい場合は
> ary a
とします。
要素一つを出力したい場合は
> ary -index 1 a
です。
変数を参照したい場合は
> print $a
とすると$aの各要素(各行)が一つの引数としてprintに渡されます。
> print $a[0]
とすると最初の一行がprintに渡されます。
3. ハッシュテーブル
ハッシュテーブルは奇数行がキー、偶数行がアイテムというテキストのイメージを格納します。
> print "key1 item1 key2 item2" | split | hash a
とすれば、ハッシュaのkey1にitem1がkey2にitem2が格納されます。
全てを出力したい場合は
> hash a
key2
item2
key1
item1
となります。ハッシュテーブルの場合は格納された順番と出力の順番は一致しません。
要素一つを出力したい場合は
> hash -key key1 a
item1
です。
変数を参照したい場合は
> print $a[key1]
とすると$aのキーkey1の要素がprintに渡されます。
あと変数の種類にはローカル変数とグローバル変数(オブジェクトの属性)とがあります。
変数を参照する場合、ローカル変数->カレントオブジェクトの属性->カレントオブジェクトの親の属性....
と検索されます。カレントオブジェクトについては次章を読んでください。
ローカル変数を作るには
> ls | var -local a b c
と-localオプションを使って作ってください。ローカル変数は
1. スクリプトファイルのload時 2. メソッドが呼ばれたとき 3. クラスが呼ばれたとき
に初期化されます。またコマンドラインではコマンドが実行されるたびにローカル変数は初期化されます。
> ls | var -local a b c
> var a b c
not found variable
> ls | var -local a b c ; var a b c
は出力されます。
変数は変数名で内容を出力することができます。
> ls | var a b c
> a
AUTHORS
> b
LICENCES
> c
Makefile.in
1.7 オブジェクト指向
xyzshのオブジェクト指向はファイルシステムのようなものだと思ってください。
ディレクトリがオブジェクトでその中にどんどん他のオブジェクト(文字列や配列オブジェクトなど)を追加していくことができます。
> object a # カレントオブジェクトにobject aを作成する
> a::run ( ls | ary A ) # lsの出力結果をオブジェクトaにAとして追加する
> a::A
lsの出力
a::runはカレントオブジェクトをaとしてブロックを実行します。よってlsの出力結果は配列Aとしてaに追加されます。
ではobject aはどこに追加されるのでしょうか?それはカレントオブジェクトです。初期値の場合はルートオブジェクトとなります。
この場合はカレントオブジェクトにオブジェクトa(中には配列Aが入っている)が追加されました。
カレントオブジェクトは
> pwo
で確認できます。
カレントオブジェクトは
> co オブジェクト名
で変更できます。
上の例で行くと
> co a
でカレントオブジェクトをaに変更できます。
で、
> A
と実行すればlsの出力を得ることができます。(root::aがカレントオブジェクトなのでa::は必要なくなる)
> self
で現在のカレントオブジェクトに定義された属性の一覧を得ることができます。
カレントオブジェクトの初期値はルートディレクトリですが、ルートオブジェクトでselfを実行するとxyzshの組み込みの内部コマンドや基本的な外部コマンドが定義されているのが分かると思います。
オブジェクトの参照順はローカル変数->カレントオブジェクト->親のオブジェクト ... -> ルートオブジェクト
となっているので、子供のオブジェクトがカレントオブジェクトでも、ルートオブジェクトにある内部コマンドは参照することができます。
また全てのオブジェクトには
run メソッド
と
showメソッド
と
selfオブジェクト (そのオブジェクト自身のリファレンス)
と
rootオブジェクト (ルートオブジェクトのリファレンス)
と
parentオブジェクト(親オブジェクトのリファレンス)
を含みます。よって
> co parent
で親オブジェクトに移動することができます。
定義したオブジェクトや配列などは
> sweep オブジェクト
で削除することができます。
1.8 関数(メソッド)
関数はdefコマンドによって定義されます。defが実行されたカレントオブジェクトに関数が追加されます。
> def fun ( print fun \n)
> fun
fun
> self | egrep ^fun:
fun: function
関数に渡された引数はローカル変数のARGV配列に入っています。
> def fun ( print $ARGV )
> fun aaa bbb ccc
aaa bbb ccc
関数に渡されたオプションはローカル変数のOPTIONSハッシュテーブルに入っています。
> def fun ( hash OPTIONS )
> fun -abc -def
-abc
-abc
-def
-def
defには-option-with-argumentオプションを取ることができ、
> def fun -option-with-argument abc,def ( hash OPTIONS )
とすれば、-option-with-argumentで設定された引数は次の引数を取るようになります。
> fun -abc aaa -def bbb -ghi
-abc
aaa
-def
bbb
-ghi
-ghi
1.9 クラス
クラスは関数とほとんど同じで呼ばれると実行されます。
クラスはclassコマンドで定義します。
> class klass ( print aaa\n )
> klass
aaa
> class klass ( ls | ary AAA )
> object a
> a::run klass
オブジェクトaにAAA配列オブジェクトが追加される。
もしmainという名前のオブジェクトがオブジェクトで定義されていたら、オブジェクトをコマンドとして呼び出した場合、そのmainというオブジェクトが実行されます。
> object a ( def main ( print called \n ) )
> a
called
2.0 コマンド置換
ブロックの出力をコマンドに貼付けます。
> print $(ls)
main.c
sub.c
$$()によるコマンド置換は各行を一つの引数として貼付けます。
> def fun ( ary -size ARGV )
> fun $$(print aaa\nbbb\nccc\n )
3
行の区切りは$$a(), $$m(), $$w()を使うことによってそれぞれ、ベル、CR、CRLFと返ることができます。
> def fun ( ary -size ARGV )
> fun $$a(print aaa bbb ccc | split -La)
3
> fun $$m(print aaa bbb ccc | split -Lm)
3
> fun $$w(print aaa bbb ccc | split -Lw)
3
2.1 例外
tryコマンドは例外処理を扱えます。tryの第一ブロックでエラーが起きるとエラーが起きた時点で直ちに第二ブロックが実行されます。
起こったエラーはerrmsgコマンドによってエラーメッセージを得て知ることができます。
> try ( print ) catch ( errmsg | if (|=~ "invalid command using") ( print get invalid command using \n ) else (print another error \n))
raiseコマンドによってエラーを起こすことができます。
> try ( make || raise "failed on make"; sudo make install) ( print make failed\n )
2.2 リファレンス
refコマンドはリファレンスを扱うことができます。
ref 変数名で、その変数のアドレスを出力できます。
> ls | var a b c; ref a b c
0x12345678
0x23456789
0x34567890
逆に
> print 0x12345678 | ref x
として入力を受け取るとそのアドレスのオブジェクトをxにバインドすることができます。
つまり
> ls | var a b c; ref a b c | ref x y z
とするとaとx, bとy,cとzは同じものとなります。
アドレスはxyzshによって保護され不正なアドレスははじくようになっています。
2.3 外部コマンド
外部コマンドは起動時にrehashされてsysオブジェクトの中に外部コマンドオブジェクトとして入れられます。
rehash内部コマンドが実行されるたびにsysオブジェクトの中に新しく作られます。
> sys::dmesg # dmesg外部コマンドを実行
ただし、lsなど使用頻度が高いコマンドについてはリファレンスによってルートオブジェクトに登録されています。
よってlsなどはsys::を付けなくても実行できます。
> ls # sys::lsでなくても良い
sys::を省略したい外部プログラムを追加するには~/.xyzsh/programにあるプログラム一覧にプログラム名を追加してください。その後rehashをすると追加されます。
> print dmesg\n >> ~/.xyzsh/program
> rehash
> dmesg
2.4 サブシェル
複数の文を一つのコマンドのように扱えることができます。
(pwd; echo aaa ; print bbb\n) | less
pwd, echo aaa, print bbb\nの出力が次のlessコマンドに渡される。
コンテキストパイプには前のコマンドの出力が入っています。
pwd | ( | less; | less; | sub -global . (|uc))
pwdの出力を2回lessして、最後に全ての文字を大文字にして出力する
2.5 コメント
# から次の改行までがコメントとなります。
> ls -al # --> ファイルの一覧の出力
2.6 チルダ展開
ほぼbashと同じ用法で使えます。
2.7 グロブ
ほぼbashと同じ用法で使えます。glob(3)を使っています。
2.8 ヘアドキュメント
ヘアドキュメントは改行を含む文字列を扱いたい場合に便利な用法です。
> print <<<EOS
aaaa
bbbb
cccc
EOS | less
<<<EOSの次の行から最初にEOSが来る行までが一つの文字列として扱われます。
lessは
aaaa
bbbb
cccc
と改行を含む文字列を受け取れます。
ただしbashのように
print <<<EOS | less
aaaa
bbbb
cccc
EOS
とは使えません。あしからず。
ヘアドキュメントの中でも変数は展開できます。
> ls | var a b c;
print <<<EOS
$a
$b
$c
EOS | less
lessは
$aの中身
$bの中身
$cの中身
を受け取れます。
変数を展開したくない場合は
> print <<<'EOS'
$a
$b
$c
EOS | less
とシングルクォートを使ってください
2.9 グローバルパイプ
文の最後に|>をつけるとグローバルパイプへの書き込みとなります。
> ls |>
書き込んだグローバルパイプは、文の最初に|>をつけると読み込めます。
> |> print
main.c
sub.c
sub2.c
> |> print
main.c
sub.c
sub2.c
追記は|>>です。
> print aaa\n |>
> print bbb\n |>>
> |> print
aaa
bbb
3.0 行コンテキストパイプ
コンテキストパイプの次に数字を指定すると読み込む行数を指定できます。
> print aaa bbb ccc | split | (|1 print)
aaa
> print aaa bbb ccc | split | (|2 print)
aaa
bbb
> print aaa bbb ccc | split | (|1 print; |1print; |1print; |1print)
aaa
bbb
ccc
return code is 262
> print aaa bbb ccc | split | (|-1 print )
aaa
bbb
ccc
> print aaa bbb ccc | split | (|-2 print )
aaa
bbb
> print aaa bbb ccc | split | (|-2 print; |-1 print )
aaa
bbb
ccc
3.1 リダイレクト
リダイレクトには>,>>,<が使えます。
> ls | write AAA
> cat <AAA
main.c sub.c fix.c
3.2 エラー出力
エラー出力をファイルに書き込む場合は"write"コマンドと"-error"オプションを使ってください。
> ./output-error-program
data
error-data
> ./output-error-program | write -error data-from-error
data
> ./output-error-program | write data
error-data
> ./output-error-program | write -error data-from-error > /dev/null
エラー出力を読み込むには"print"コマンドと"-read-from-error"オプションを使ってください
> ./output-error-program | print -read-from-error
error-data
> ./output-error-program | (|print -read-from-error; | print ) | less
error-data
data
> ./output-error-program | (|print -read-from-error | less; | print | less)
error-data
data
3.3 複数行のテキストの取り扱い
xyzshで複数行のテキストを一つの変数に入力したい場合は-Laオプションを使ってください。
\aが行の区切りとして扱われ、結果\n,\r\n,\rなどを単なるテキストとして処理でき、複数行のテキストを扱えます。
少し複雑なコードとなりますがpコマンドでデバッグすれば、理解できるかと思います。
3.4 コマンドライン補完
コマンドライン補完にはreadlineを使っているので、ほぼbashと同じように使うことができます。
ユーザー定義の補完も作ることができcompletion.xyzshで内部コマンドのユーザー定義補完が定義されています。
ユーザー定義の補完はroot::complにオブジェクトとして登録されています。
C-xでマクロによるコマンドの出力結果をコマンドラインに貼付けることもできます。~/.xyzsh/macroを修正してください。
括弧の対応のチェックには~/.inputrcにset blink-matching-paren onを書いておいてください。
rlオブジェクトを使って補完のプログラム中にreadlineを操作できます。rl::helpを見てください。
3.5 ヘルプ
help コマンド名
でコマンドの使い方を見ることができます。各コマンドのオプションなどはそちらで調べてください。
3.6 C拡張ライブラリ
C拡張ライブラリはload -dynamic-libraryを使って呼び出せます。サーチパスは/usr/local/lib/xyzsh/"ファイル名"(xyzshをインストールしたディレクトリ/lib/xyzsh/"ファイル名")を探して無いなら、~/lib/"ファイル名"を探し、それでもないなら、"ファイル名(絶対パス、または相対パス)"を探します。拡張子は省略してはいけません。
作り方について。migemoがC拡張ライブラリとなってます。詳しくはsrc/ext/migemo.cを見てください。dl_initとdl_finalを定義する必要があります。dl_initはload -dynamic-libraryをするときに呼ばれます。dl_finalはxyzshが終了するときに呼ばれます。拡張ライブラリのコンパイルは
gcc -shared src/ext/migemo.c -o src/ext/migemo.so -lmigemo -lxyzsh $(LIBS) $(CFLAGS)
などとすれば良いです。$(LIBS)と$(CFLAGS)は自分で定義する必要があります。
拡張ライブラリと同じ名前 + .xyzshのソースファイルはload時に自動的に読み込まれます。ヘルプや補完などをそこに書くことができます。
3.7 xyzshの組み込みについて
xyzshのメモリ管理について。xyzshはGCとスタックとmalloc&freeの3つのオブジェクトのメモリ管理を行なってます。しかし、以下の制限があります。
GCで作り出されるオブジェクトはマークをさせるためにgRootObjectかgXyzshObjectから辿れるようにしなければなりません。(GCで管理されてgRootObjectかgXyzshObjectから辿れるコンテナオブジェクトに格納される必要がある)。でなければ、sweepで消されてしまいます。sweepは一定回数コードを実行すると自動的に実行されます。
GCで作り出したコンテナオブジェクトにはGCで管理されるオブジェクトしか格納できません。同じようにスタックで作り出したコンテナオブジェクトにはスタックで管理されるオブジェクトを、MALLOCで作り出されるコンテナオブジェクトにはMALLOCで管理されるオブジェクトを格納してください。
ユーザーによって定義される外部オブジェクト(EXTOBJ)の構造体内のオブジェクトではmallocによるオブジェクトの管理を行なってください。構造体内のオブジェクトをGCで管理を行なってmMarkFunでそのオブジェクトをマークしてもGCのオブジェクトのsweep順によっては正しく動作しません。セグメンテーションフォルトが起こる可能性もあります。ただし、blockオブジェクトはmallocによって管理できないので、blockオブジェクトは手動でマークしてください。stringオブジェクトもGCによって管理できるはずです。