Cシェルプログラミング --- 対話的処理とシェルスクリプト --- 京都大学(高崎金久) 目次 1.なぜシェルプログラミングをとりあげるか? 2.シェル変数を使ってみよう 1)シェル変数とは? 2)シェル変数の設定.解除 3)変数の値の参照 4)配列の要素の参照 5)@ コマンド 3.echo コマンドとその周辺 1)echo コマンドの正体 2)引用符の使い方と意味 3)特殊文字 4.繰り返しと条件分岐を利用してみよう 1)foreach コマンドによる繰り返し 2)ファイル名の一部を取り出す方法 3)while コマンドによる繰り返し 4)if コマンドによる条件分岐 5)さまざまな条件式 6)break コマンドと continue コマンド 7)exit コマンド 8)その他の制御構造 5.スクリプトファイル 1)対話的処理とバッチ処理 2)シェルスクリプトの書き方 3)スクリプトの実行の方法 4)スクリプトの中でのみ使える特殊な変数 5).login, .cshrc, .logout のカスタマイズ --------------------------------------------------------------------- 1.なぜシェルプログラミングをとりあげるか? プログラミングはなにもC言語のような本格的なプログラミング言語を用いて 行うことばかりではありません.日常的に出会う問題はもっと細々したことが 多く,そういう仕事を処理するのにいちいちCでプログラムを書くのは大げさ です.こういうときに役に立つのがシェルでのプログラミング(シェルプログ ラミング)です.ここではCシェルでのシェルプログラミングをとりあげます. *ただし、Web (WWW) の cgi スクリプト用にCシェルのプログラムを 利用することはおすすめできません。あとで説明するように、Cシェル のプログラムはファイルに保存しておいて必要なときに呼び出すという 使い方もできますが、Cシェルのプログラミング機能はかなり貧弱で、 問題点も多く、cgi スクリプトに利用すると安全面の抜け穴(セキュリ ティホール)が生じやすいからです。cgi スクリプトの作成には perl などのより記述能力の高いスクリプト言語やC言語など本格的なプログ ラミング言語を使ってください。 シェルはユーザーと計算機システムの仲立ちをしています.プロンプトマーク を表示してキーボードから入力を受け取ったり,コマンドを解釈して必要な機 械語プログラムを実行したり,結果を画面に表示したり,という仕事をしてい るのがシェルです.シェルはUnix本体(カーネルと言ったりします)とは 独立の機械語プログラムです.Unixシステムでは幾種類かのシェルが用意 されていて,ユーザーは自分の好みのシェルを選ぶことができます.標準的に 用意されているのはBシェル("sh")とCシェル("csh")で,それ以外にB シェル系の"ksh", "bash", Cシェル系のTCシェル("tcsh")などがよく用 いられます.   *Bシェル系かCシェル系かはプロンプトマークを見るとわかります.   (よほど変な設定をしていない限り)Bシェル系のプロンプトマークは   $ , Cシェル系のプロンプトマークは % であることが多いからです. 普通,このシェルに対する指示は単一のコマンドか,せいぜいそれらをパイプ やグルーピングでつないだものが多いのですが,実は変数を使ったり算術計算 を行ったり繰り返しや条件分岐を行ったりすること -- つまりプログラミング -- もできるのです.このプログラミング機能を利用すれば,Unixシステ ムに用意されている道具を組み合わせて複雑な処理を行うことが可能になりま す. もちろんシェルプログラムは本格的なプログラミング言語に比べればかなり貧 弱なものです.そのかわり気楽に使えますし,Unixのシェルプログラミン グはパソコンの場合(たとえばMS−DOSのいわゆるバッチファイルのプロ グラミング)に比べればはるかに強力です. シェルプログラミングを知っておくことにはもっと基本的な意味もあります. ユーザーのホームディレクトリにはたくさんの初期設定用ファイル("." で始 まるファイル名をもつ)がありますが,そのうちのいくつかは実はシェルプロ グラムです.このようにファイルに書き込まれたシェルプログラムをシェルス クリプト,あるいは単にスクリプトといいます.(スクリプトというのは英語 で台本のことです.MS−DOSではバッチファイルのことです.)ユーザー が自分の環境設定を好みのものに変えるためにはこれら初期設定用シェルスク リプトを書き換えなければなりません.そのためにはシェルプログラミングの ことを(多少は)知っておかなければなりません. シェルにはいろいろ種類があるのでどのシェルでプログラミングの練習をする か迷うところですが,上に掲げたうちでポピュラーなのはCシェル系なので, Cシェル系を取り上げることにしました. CシェルやTCシェル以外のシェルを使っている人もこの練習のためにわざわ ざログインシェルを切り替える必要はありません.マンドラインから"csh" あ るいは"tcsh"と入力すればこれらのシェルが起動して以下の練習の環境が整い ます."exit" コマンドでもとのシェルに戻ります.(裏返せば,普段はCシ ェルを使っているが別のシェルの練習もしてみたい,と言う人も同じように別 のシェルを起動できるということです.)もっとも,ログインシェルをBシェ ル系に切り替えているほどの人はこんなことは先刻承知でしょう. なお,Cシェル系のログインシェルを使っている人でも,Bシェルのことを知 らなくていいというわけではありません.UnixではBシェルのほうが基本 的で,かつプログラム能力も優れています.システム管理者を目指す人はBシ ェルプログラミングも勉強する必要があります.   *Unixではシェル以外にもスクリプトによるプログラムが可能な道具   がいろいろあります.sed, awk などのテキストフィルターや,perlなど   のスクリプト言語です.sed のスクリプトはプログラムというよりは   書き換えルールの羅列ですが,awk や perl のスクリプトはごく普通の   意味のプログラムで,機能や記述能力の点ではシェルスクリプトよりも   優れています.興味のある人は自分で勉強してください. 2.シェル変数を使ってみよう 1)シェル変数とは? シェルがプログラミング環境でもある証拠の一つは変数が使えることです.シ ェルのなかで使える変数をシェル変数といいます.なお,これとよく似たもの として環境変数というのがあります.MS−DOSでは環境変数とよばれるも のしかありませんが,Unixではシェル変数と環境変数は別のものです.そ の違いを一言で言えば,シェル変数はシェルが使うもので,環境変数はコマン ドにより呼び出されたプロセス(例えば,エディターなど)が使うものです. シェル変数の一部はシェルにとっての環境変数の役割を果たします.実際ログ イン直後でもすでにいくつかのシェル変数が設定されています.それを見るに は"set" というコマンドを使います.(なお,個々の変数の値を見るにはあと で述べる "echo" というコマンドを使う方が便利です.)   [練習]set コマンドを実行してみること.1画面に収まらないときには   set | less を使うとよい. このときに表示されるのが現在のシェル変数の状況です.ログイン時に設定さ れたシェル変数を勝手に変えるとトラブルの原因になりますので,シェルプロ グラミングで変数を使うときにはこれらの変数と重ならない変数名を使います. ちなみに,環境変数は "env"で表示できます.   [練習]env または env | less を実行してみること. 全然違うでしょう?(なお,システムによっては "env" の代わりに "printenv" というコマンドになっている場合もあります.) いずれの場合も,各行の左側にあるのが変数名,右側にあるのが現在の値です. (シェル変数には値が空欄になっているものあるはずですが,それらは変数名 がシェルに登録されていることのみが意味をもつ変数です.)なお,表示を見 れば気がつくことですが,シェル変数名には英小文字を,環境変数には大文字 を使うのが習慣になっています. ここのシェル変数や環境変数の値をみるには "echo" コマンドを用います.そ の使い方は次の例を見てください.特に変数名に$ をつけていることに注目. (その意味は追々わかります.)   [例]echo $path ; echo $PATH(シェル変数 path と環境変数 PATH の   現在の値を表示する) 2)シェル変数の設定.解除 シェル変数を設定して値を代入したり,値を変更したりするにはやはり "set" コマンドを使います.また,シェル変数の設定を解除するには "unset" コマン ドを使います.書式は次の通りです(name は変数名,value は値).   書式1.set name (変数の設定のみを行う.値は空.)   書式2.set name=value(変数を設定して値を代入する,あるいはすでに       設定された変数の値を変更する.)   書式3.unset name (変数の設定を解除する) シェル変数に代入できるのは文字列または文字列の配列です.配列というのは 複数の値を並べて一つにしたもので,ベクトルのようなものと思えばいいので すが,今の場合,要素として入っているのは文字列です.   [例]set bindir=/usr/local/bin   [例]set bindir=(/bin /usr/bin /usr/local/bin $HOME/bin) 一つ目の例では bindir というシェル変数に"/usr/local/bin" という文字列 を代入しています.二つ目の例では同じシェル変数に"/bin", "/usr/bin", "/usr/local/bin", "$HOME/bin" の4つの文字列からなる配列を代入していま す.(なお,ユーザーのシェル環境を設定するシェル変数のうち path という 変数はこの例のようにディレクトリ名を並べた配列として設定されています. 各自の設定を調べてみて下さい.) 3)変数の値の参照 変数は値を参照できないと意味がありません.変数の値は変数名に $ という 記号をつけることで参照できます.   書式1.$name (単独で使用するときに用いる形)   書式2.${name} (引き続いてほかの文字が続く場合などに用いる形) { } の有無はこれだけではわかりにくいので次の例をよく見てください.   [例]set prerix=/usr/local set prefix2=$prefix      set bindir=${prefix}/bin      set mandir=${prefix}/man/man1 2行目のように,参照した値はそのまま文字列として扱えますし,3・4行 目のようにほかの文字列とそのまま連結することもできます.{}を使うのは 連結する文字列との境を明確にする必要のある場合です.(なお,{$bindir} などとしてしまいがちなので,注意.)単独で使う場合でも{}をつけても構 いません(無害です.)上のコマンドを実行した段階での各変数の値は prefix == /usr/local prefix2 == /usr/local bindir == /usr/local/bin mandir == /usr/local/man/man1 となっています.(ただし == は等号の意味で使っています.)   [練習]実際に上のように変数を設定し,echo コマンドで設定を確認   してみること.また   (1)ls $prefix   (2)ls ${prefix}/lib | less   などのコマンドを実行して見ること. $prefix はシェルによって /usr/local と解釈されます.だから上の(1) と(2)はそれぞれ   (1)ls /usr/local   (2)ls /usr/local/lib | less と解釈され実行されます. この例ではあまり有難みがわからないかもしれませんが,長くて複雑なパス 名を何度も繰り返して入力しなければならない場合,それを適当なシェル変 数に代入してシェル変数名で使うと,間違いが少なくなります.これは結構 実用になる技法です.(ソフトウェアのインストールなどにも利用されてい ます.) 4)配列の要素の参照 配列型の値をもつ変数の要素および要素数はつぎのようにして参照できます:   $name[n] または ${name[n]} : n 番目の要素(ただし左端は1番目)   $name[m-n] または ${name[m-n]} :m 番目からn 番目までの要素からなる   部分配列   $#name または ${#name} : 全要素数 ({}は前と同様,ほかの文字列との区切りのために用います.)たとえば set bindir=(/bin /usr/bin /usr/local/bin $HOME/bin) と設定されているときには $bindir[1] == /bin $bindir[2] == /usr/bin $bindir[3] == /usr/local/bin $bindir[4] == $HOME/bin $#bindir == 4 です.実際に echo コマンドで確かめてください.ところで echo $bindir[4] とすると,何か表示が変だと思いませんか?$HOME という文字列の代わりに 別の文字列が表示されているはずです.実は HOME はユーザーのホームディ レクトリを格納した環境変数です.その値が表示されたのです. 5)@ コマンド 本来,シェル変数の値は文字列または文字列の配列に限ります.しかし @ コ マンドを使えば,数値を代入したり算術演算もできます.(ただし,本当は数 値ではなくて,あくまでも,0,1,2,...という「文字」です.) @ コマンドの使い方は set と同様です(@ と変数名のあいだに空白を忘れな いで下さい):   書式1)@ (設定されているすべてのシェル変数とその値を表示)   書式2)@ 変数名 = 式(算術式の値を求めて変数に代入する)   書式3)@ 変数名[添字] = 式(算術式の値を求めて配列要素に代入する) 右辺の式には数値,数値を代入した変数,それらを +(加算), -(減算), *(乗算), /(除算), %(剰余算)(このほかにも若干ある)などの演算子 および丸括弧()で結んだ数式が許されます.   [例]@ b = 1; @ b = 1; @ c = $a + $b; echo "$c" さらに,C言語風の算術代入文とインクリメント・デクリメントも使えます.   +=  左辺の変数に右辺の式を加算して左辺の変数に代入する   -=  同様に減算して代入   *=  同様に乗算して代入   /=  同様に除算して代入   %=  同様に剰余算して代入   ++  変数の値を1だけ増加(インクリメント)   --  変数の値を1だけ減少(デクリメント)   [例]@ a *= 2 (a を2倍する)   [例]@ i++ (i を1だけ増やす) これで電卓のかわりになるかどうかわかりません(実験してみて下さい). 数値変数の本来の用途は繰り返しの制御や個数の勘定等にあります. 3.echo コマンドとその周辺 1)echo コマンドの正体 以上のような例から察せられるように,echo $name で変数 name の値が表示 されるのは特別な仕掛けがあるからではなくて,echo が単に文字列を画面に 表示するシェルコマンドだからです. たとえば echo 'Hello, world!' というコマンドを実行してみて下さい. Hello, world! という文字列が次の行に表示されたと思います.echo $path というコマンドも本質は同じことで,シェル変数 path の値が文字列として echoコマンドに渡されて表示されるので,シェル変数の内容表示として機能 するわけです. 2)引用符の使い方と意味 変数に代入したり,echo に渡したりするのはすべて文字列ですが,Cシェル の文字列は正式には引用符でくくったものです(くくらなくてもよいことも あるわけですが).だから前の例で prefix = "/usr/local" あるいは prefix = '/usr/local' としても意味は変わりません.(代入に際して引用符自体は無視されます.) ところがややこしいことに,1重引用符と2重引用符は一般には意味がちが います.その違いは上の例では見えませんが,引用符の中にシェル変数が入 ってくるときに現われます.次の例を見て下さい.   [例]echo "I am $user. My home directory is $HOME."   [例]echo 'I am $user. My home directoru is $HOME.' 引用符に入っている変数はログイン時にすでに設定されているはずです.2 つのコマンドの違いを実際に入力して確かめて見てください.(注意:この とき1重引用符 ' と逆引用符 ` を間違えないでください.逆引用符には別 の意味があります.)どうでしょうか?2重引用符の場合は変数の値が参照 され,1重引用符の場合は $user, $HOME という文字の並びがそのまま表示 されたと思います.これが違いです.   *1重引用符:変数の値参照が行われず単なる文字の並びとして扱われる   *2重引用符:変数の値が参照され,それに置き換えられる 逆引用符 ` ` は「コマンド置換」と呼ばれる機能をもちます.逆引用符の中 にはコマンドを書きます.するとそれが実行されてその出力結果の文字列が `...` に置き換えられます.このコマンド置換は2重引用符の中でも実行され ます.抽象的な説明ではわかりにくいので簡単な例を示します. [例] echo "present date and time is `date`" echo "present workind directory is `pwd`" date は現在の日時(もしかしたら日本語表示?),pwd は現在いるディレク トリ名を表示するコマンドですから,コマンド置換によって `...` の部分は これら日時データやディレクトリ名に置き換わります(やってみてください). 3)特殊文字 Cシェルで用いられる文字のなかには特別な意味をもつものがあります. |,(,),&,?,*,{,},[,],~,;,$,!,:,#, などです.これらの本来の意味を一時的 に失わせて使いたいことがあります.そのためには引用符 '," で囲んだり, \ を前につけます.echo のあとの文字列を引用符でくくるのもそのような 処理の一種です.   [例]echo "使い方: spell filename | sort | uniq > errorfile" 引用符を忘れたらどうなるか,試してみてください.   [例]echo \$host $host という文字列そのものが返ってきます. 引用符や \ 自体も特殊記号です.これらを単なる文字として使うには, \', \", \\ というように \ をつけます. [練習]echo で次のようなメッセージ(誤植ではない)を表示させてみよ: 引用符や \ 自体も特殊記号です.これらを単なる文字として使うには, \', \", \\ というように \ をつけます.    4.繰り返しと条件分岐を利用してみよう 1)foreach コマンドによる繰り返し プログラムに繰り返しはつきものです.Cシェルプログラムで繰り返し処理を 行うにはいくつかのやり方がありますが,一番手軽でよく使うのは foreach コマンドによる繰り返しです.これは繰り返し制御用の変数を指定された範囲 に走らせるもので,次のような構文に従います. [foreach コマンドの構文] foreach 変数名 (範囲リスト) コマンド行の並び end シェル変数はすべて文字列を値にとるということを思い出してください.範囲 リストは文字列の集まりを指定します.指定の仕方は [例]foreach pig (boo hoo woo) # 空白で区切って書きならべる [例]foreach file (*.c *.h) # ファイル名置換と同様 * は任意文字列 などとします.1番目の例では pig は boo, hoo, woo の3つの値をとります. 2番目の例では file は現在のディレクトリ中の *.c, *.h にマッチするすべ てのファイル名に渡って走ります. この例からわかるように,foreach コマンドの主な用途はいくつかのファイル (あるいはディレクトリ)に対して同じ操作を繰り返すことにあります.これ はシェルを普通に(対話的に)使っているときにも便利な技巧です.簡単な例 で説明してみましょう.(各自実際に試してみてください.) [問題]現在のディレクトリ中にある*.c というファイルの先頭部分を head で切り出し,先頭にファイル名をおいて,順に headlines というファイルに 書き込みたい.どうすればよいか? これはとにかく foreach f (*.c) で繰り返せばよいはずです.ファイルに書 き込むには追加のリダイレクト >> headlines を使います.ファイル名は echo $f で取り出せます.だから結局プログラム(これは確かにプログラムの 名に値します!)はたとえば次のようになります. [プログラム] foreach f (*.c) echo $f >> headline # ファイル名を書き込む echo "" >> headline # 空行を書き込む head $f >> headline # 先頭部分を書き込む echo "" >> headline # 空行を書き込む end ところで,これらのコマンドをどのようにして入力したらいいのでしょうか? シェルスクリプトで実行するのならば(あとで説明するように)これをそのま まエディターでファイルにすればよいのですが... 答は,そのままキーボードから入力すればよいのです.まず最初の行を入力し てリターンキーを押すと,画面は次のようになると思います: % foreach f (*.c) ? このように,次の行に?マークが表示され,カーソルがそこに移動します.こ の?マークは「コマンドがまだ完結していないよ」というシェルからのメッセ ージです.foreach コマンドは end が来て初めて完結しますから,シェルは まだ続きがあると思って待っているのです.(今までも,シェルを使っていて 何かの拍子でこういう状態になった経験があるのではないかと思いますが,そ の場合は意図せずに何か完結しないコマンドを入力していたのです.) ?マークが出たら上に示したプログラムを次々入力します.1行入力してリタ ーンキーを押すごとにカーソルは次の行へ移って?マークを表示します.最後 に end(リターン)を入力したところでプログラムの実行が開始されます: % foreach f (*.c) ? echo $f >> headline ? echo "" >> headline ? head $f >> headline ? echo "" >> headline ? end ... 2)ファイル名の一部を取り出す方法 ところで,上のようにファイル名(やディレクトリ名)で繰り返し処理をする 場合,ファイル名の一部を取り出せると都合のよいことが多いはずです.たと えば,ファイルは多くの場合,hello.c のようにピリオドで区切られた2つの 部分からなります.このピリオドの前の部分やあとの部分を切り出せると便利 です.また,ファイル(やディレクトリ)のフルパス名のうち最後のファイル の部分とその前の部分を別々に切り出したい場合もあります. Cシェルでは,ファイル名を格納した変数を参照するときに次のような変更子 をつけると,そのような切り出しができます. :r ファイル名の語幹 (root) 部分 :e ファイル名の拡張子 (Extension) 部分 :h パス名の最後のファイル名を除いた (Head) 部分 :t パス名の最後のファイル名 (Tail) 部分 [例]set f = hello.c とするとき $f:r は hello, $f:e は c となる. [例]set f = /usr/local/bin/mule とするとき $f:h は /usr/local/bin, $f:t は mule となる. 各自実験して見てください. 3)while コマンドによる繰り返し Cシェルには繰り返し処理のためのもう一つのコマンドとして while コマン ドが用意されています.これはある条件が満たされる限り何かを繰り返す, というもので,次のような構文に従います: [while コマンドの構文] while (条件式) コマンド行の並び end 条件式は1(真)か0(偽)の値をとるものならなんでも許されます.条件 式が真(1)の限りそのあとに続くコマンド(複数行可)を繰り返す,とい うのがこの while コマンドの意味です. 条件式として実際に使われるのは,文字列の比較,ファイル状態,およびこ れらを論理演算で組み合わせたものです.条件式は if コマンドによる条件 判断でも必要になるので,あとでまとめて説明することにします. シェルコマンドもこのあたりになると対話的に間違いなく実行するのは難し く,シェルスクリプトで実行するのが普通です.ここではとりあえず次のよ うな無限ループも可能である,ということのみ注意しておきます. [例]5秒おきに who コマンドを実行することを繰り返す.(注意!これは あくまで実験にとどめる.頻繁に実行するとシステムに余分な負担がかかる.) % while(1) ? sleep ? who ? end ... (Ctrl-C で中断) ここでも,最初の1行を入力したあとは?マークにしたがって入力して行き ます.最初の while (1) は条件が常に真(1)なので,以下のコマンドを 無限に繰り返す無限ループに入ります.sleep はそのあとの数字(秒)だけ 待て,というコマンドで,5秒待ってから who コマンドを実行する,という ことを繰り返します.このままではいつまでも止まらないので Ctrl-Cで強制 終了させます. このように強制終了させるのはプログラム本来の在り方からすれば邪道です ので,ループから脱出するためのコマンドとして break コマンドが用意され ています.break コマンドは if コマンドと組み合わせて使うので,あとで 説明します. 4)if コマンドによる条件分岐 条件に応じてコマンドの実行を制御したり,あるいはある条件が満たされたと きに foreach や while のループから脱出したりするために,if コマンドを もちいます.if コマンドの構文には3通りあります. [if コマンドの構文1] if (条件式) コマンド行 [if コマンドの構文2] if (条件式) then コマンド行の並び else コマンド行の並び endif [if コマンドの構文3] if (条件式1) then コマンド行の並び1 else if (条件式2) then コマンド行の並び2 else if (条件式3) then ...... else コマンド行の並び* endif 構文1は最も簡単なもので,条件式が真(1)のときにその次のコマンド行 を実行し,偽(0)のときコマンド行をスキップします. 構文2は条件式が真(1)のときと偽(0)のときとで実行するコマンド群 (複数行与えられる)を切り替えるものです. 構文3は複数の条件式の成立を順次調べるもので,条件式1が満たされれば コマンド行の並び1を実行し(あとはendif の次に飛ぶ),そうでなければ その次の条件式2を調べて,それが満たされればコマンド行の並び2を実行 し(あとはendif の次に飛ぶ),...,と進んで,どの条件式も成立しな ければ最後のコマンド行の並び*を実行します. なお,この if コマンドの構文はCの if 文とよく似ていますが,まったく 同じというわけではないので(たとえばCでは then は使わない),注意が 必要です. [例] foreach f (*) if (-d $d) then echo "$f is a directory" ls -F $f else if (-z $f) then echo "$f is a file of zero size" else echo "$f is a plain file" endif (-d $f), (-z $f) はファイルの状態を調べる条件式で,それぞれ,ファイル がディレクトリファイルであるとき真(1),大きさ0のファイルであるとき 真(1)の値をとります. [例] if ($name == "boo") then echo "I am the eldest" else if ($name == "hoo") then echo "I am the second eldest" else if ($name == "woo") then echo "I am the youngest" else echo "Who am I?" endif 等号 ==(代入の = ではない)は文字列が一致するかどうかを調べるもので, たとえば $name == "boo" は $name が "boo" に等しいとき真(1)の値をとります. 5)さまざまな条件式 上の例に見られるように,条件式にはファイルの状態を調べるものと,文字列 の比較を行うものの2種類があります.また,これらを論理演算で組み合わせ ることができます. [ファイルの状態を調べる式] -r ファイル名  ファイルが存在してユーザーの保護モードが r のとき真 -w ファイル名  ファイルが存在してユーザーの保護モードが w のとき真 -x ファイル名  ファイルが存在してユーザーの保護モードが x のとき真 -e ファイル名  ファイルが存在するとき真 -o ファイル名  ファイルが存在してユーザーが所有者のとき真 -z ファイル名  ファイルが存在して大きさが0のとき真 -f ファイル名  ファイルが通常のファイルのとき真 -d ファイル名  ファイルがディレクトリファイルのとき真 [文字列の比較式] 文字列1 == 文字列2 文字列が等しいとき真 文字列1 != 文字列2 文字列が等しくないとき真 文字列1 < 文字列2 数値としての大小 文字列1 > 文字列2 同上 文字列1 <= 文字列2 数値として等号を含む大小 文字列1 >= 文字列2 同上 文字列1 =~ 文字列2 文字列2をファイルのパターンと見て等しいとき真 文字列1 !~ 文字列2 文字列2をファイルのパターンと見て等しくないとき真 ファイルのパターンと見る,とは *.c(* は任意の文字列),96iex?.doc(? は任意の1文字),temp-[a-g].doc([a-g] は a,b,c,d,e,f,g のいずれか) という指定ができるということです. [論理演算] 式1 && 式2 (AND) 式1と式2がともに真のとき真 式1 || 式2 (OR) 式1または式2が真のとき真 ! 式     (NOT) 式1が偽のとき真 &&, || は2個以上つなげることも可能ですが,そのときには左から順に2つ ずつまとめて評価されます.評価の順序を変えるには丸括弧 () でくくります. たとえば式1&&式2||式3 はそのままでは ((式1&&式2)||式3) とみなされ ます.評価の順序を変えるには (式1&&(式2||式3)) などというように括弧 でくくります. [例] if ((-f $f) && (-x $f)) ... # $f が通常の実行可能ファイルのとき... 6)break コマンドと continue コマンド foreach, while のループから抜けるには break コマンドをもちいます.また ループの中のそれ以後のコマンドをスキップしてループの最初にもどり続行す るには continue コマンドを用います. [例] set files = 0 set dirs = 0 set executables = 0 set dotfiles = 0 foreach f (* .*) if (-d $f) then @dirs++ continue else @files++ endif if (-x $f) @executables++ if ($f =~ .*) @dotfiles++ end echo "directories: @dirs" echo "files : @fiels" echo " executable -- @executables" echo " dot -- @dotfiles" ファイルやディレクトリの数を勘定しています.Cシェルは構文解析が十分で ないので,if コマンドを何重にも入れ子にするとうまく動作しなくなります. そのため if (-x $f) ..., if ($f =~ .*) ... の部分は if ... endif の外 に出しましたが.(この程度ならば else の部分に入れてしまっても大丈夫か も知れません.その場合,continue コマンドはいらなくなります.) 7)exit コマンド exit コマンドはプログラムの実行自体をそこで終了(中断)するためのコマ ンドです. [構文] exit(n) # n は0以上の数値 数値はスクリプトの終了の際にシェルに終了状態値として渡されます.通常, 正常終了のときには0を,エラーのときには0以外の値を返すのが習慣です. シェルスクリプトのみならずさまざまなコマンド(機械語プログラム)は終了 状態値を返すようになっているものが多く,返された終了コードはシェル変数 status に格納されています. シェルスクリプトも含め一般にコマンド終了時の exit 値をシェルプログラム のなかで利用するにはコマンドを(必要ならば引数もつけて) { } で囲みます. これはコマンドを実行してその値を返す式です.これを適当なシェル変数で受 けてやります. [例] @ ret = {aaa -x} # コマンド "aaa -x" を実行して結果を $ret に格納 8)その他の制御構造 繰り返しや条件分岐などはプログラムの流れを変えたり制御したりするものな ので,一般に制御構造と呼ばれます.制御構造がなければ,プログラムは書き 並べられたコマンドを最初から順に実行して行くだけです.Cシェルプログラ ミングにはここに紹介した以外にもいくつかの制御構造が用意されていますが, ここでは最後に switch 文だけ紹介します. switch 文は任意個の選択肢をキーワード(文字列)で指定するものです.if 文や while 文のように条件式を調べる制御構造とは少し感じが違いますが, 選択肢が多数にわたるときには有用です. [switch 文の一般的構文] switch (キー) case 文字列1: コマンド breaksw case 文字列2: コマンド breaksw ... default: コマンド breaksw endsw [機能] キーとして与えられる文字列(多くはここにシェル変数を置く) をそのあとの文字列1,文字列2,...と順次比較し,一致 したところでそこに記述されているコマンドを実行する.一致 するものがなければ最後のデフォルトのコマンドを実行する. パターンマッチを行う文字列1,文字列2,...にはファイ ル名のパターンマッチと同様のワイルドカード (*,?) が使える. 5.スクリプトファイル 1)対話的処理とバッチ処理 プログラミング環境として見るとき,シェルはいわゆるインタープリター(解 釈実行系)です.インタープリターというのはコマンドやプログラム(プログ ラムというのは結局は多数のコマンドの集まりです)を受け取って,その意味 を解釈し,指示された仕事を行うものです.Basic, Lisp, scheme, Prolog は インタープリターです.インタープリターによるプログラミングでは処理の実 行の主体はインタープリター自身です.したがってインタープリターには,プ ログラムを入力し,適当な形で記憶したりファイルに保存し,さらにその意味 を解釈して処理を実行する,というような機能が必要です.いいかえれば,イ ンタープリターというのはそれ自体が一つのミニ計算機システムのようなもの なのです. これに対してFortran, Pascal, C, C++ などはいわゆるコンパイラー(翻訳系) の形をとっています.コンパイラーはプログラミング言語で書かれたプログラ ムを機械語プログラム(計算機システムが直接メモリーに読み込んで実行でき るプログラム)に変換するのが主な仕事です.いったん機械語プログラムがで き上がれば,作業はコンパイラーの手を離れます.機械語プログラムの実行は 例えばユーザーがコマンドとして呼び出すことによって行われます.呼び出さ れた機械語プログラムの実行主体はUnixなどのオペレーティングシステム です. シェルによるコマンド・プログラムの処理の仕方には対話的処理とバッチ処理 の2通りの方法があります(これは一般に計算機による処理全般についても用 いられる用語です).今までシェルのプログラミング機能の一部を見てきまし たが,これはキーボードからコマンドを入力し,その結果を確かめてから次の コマンドを入力する,という対話的(インタラクティブな)処理でした.これ に対して,実行するコマンド群をあらかじめ全部ファイルに記述しておいて, 一括して実行させるやり方もできます.これをバッチ処理といいます.(MS −DOSのバッチファイルという言葉もここから来ています.) 対話的処理とバッチ処理はそれぞれ一長一短がありますので,場面に応じて使 い分けるとよいでしょう.一般に,対話的処理はプログラムそのものがあとに 残らないので,その場限りの簡単な処理やスクリプトファイルを作成中の部分 的試験などに用いられます.これに対して,バッチ処理はスクリプトファイル を作成して行いますので,でき上がったスクリプトを再利用できます.また, 大量のコマンドを間違いなく入力しなければならない場合とか,一つ一つのコ マンドの実行に時間がかかるような場合(例えば大きなソフトウェアのインス トール作業)には,たとえその場限りの処理であっても,スクリプトファイル を作成してバッチ的に処理するほうが安全です.   *なお,1行で書けてしまうが直接入力するのが面倒くさい,という   ようなコマンドをスクリプトにするのはディスクスペースの無駄です.   そういうものはむしろ alias(別名)として定義するのがよいのです. 2)シェルスクリプトの書き方 書き方といってもあまり特別なことはありません.対話的にコマンドを打ち 込んで行くのと同じことをファイルに(改行しながら)書き込んで行くだけ です.ただし最初の行だけは # で始まる特別な書き方をしなければなりま せん.つぎの例のどれか(いまはどれでもよい)に従ってください: [1行目の形式1]# [1行目の形式2]#!/bin/csh [1行目の形式3]#!/bin/csh -f いずれも # が1文字目に来るようにしなければなりません. 形式2はこのスクリプトがCシェルで実行されることを意味しています. 形式1はその省略形です.実はエディターなどと同様,Cシェルも機械語 プログラムで,その実体は /bin/csh というファイルなのです.(ちなみ に,Bシェルは /bin/sh, TCシェルは /usr/local/bin/tcsh です.) 形式2はCシェルを新たに起動してそのうえでスクリプトとして実行せよ, という指示です.また,形式3はそのときにCシェルを -f オプション (起動時に初期設定ファイル .cshrc を読み込まない)で起動せよ,とい う指示で,実行が幾分早くなります(と言っても僅差ですが). ちなみに,1行目が空行のときは #!/bin/sh,Bシェルで実行せよ,とい う意味に解釈されます. なお,形式2のような書き方はスクリプトファイル全般に用いられるもので, awk スクリプトや perl スクリプトなどでも同様です. 1行目の # にはこのように特別な意味がありますが,それ以外の # はコメ ントの開始記号として解釈されます.つまりそこから行末までは無視されて コマンドの実行に影響を与えません. [簡単なCシェルスクリプトの例] # # Example # echo "こんにちは,このホストは $host です" echo "現在の日時は `date` です" cal who mule -geometry 80x40-20-20 & こういうものをエディターなどで書いてファイルに保存します. 3)スクリプトの実行の方法 シェルスクリプトファイルの名前がたとえば hello であったとします.   *注意:シェルスクリプトファイルの名前は既存のコマンド名などと   重ならないように選んでください.たとえばよくあるミスは test   というファイル名をつけてしまうことです.実は test というコマン   ドがすでにあります. これを実行するには次のような方法があります. [実行方法1]csh hello というコマンドを実行する.実行しないで エラー等があるかどうかのみ調べるには csh -n hello とする. -- この方法はスクリプトがまだ完成していない段階で,テストをするのに 向いています. [実行方法2」hello の保護モードを   chmod +x hello というコマンドで実行可能にしてから, hello (あるいは ./hello) とファイル名をコマンドとして呼び出す.(ユーザーの path 変数の設定 の具合によっては後者のようにしないと呼び出せないことがある.) -- この方法はいわば完成版をつくるものです.ファイルの保護モードは ls -l で調べられます.x (実行可能)の状態になっていれば,ファイル 名を呼び出すだけで実行されます.chmod コマンドはそのためのものです. 4)スクリプトの中でのみ使える特殊な変数 mule hello というようにファイル名を mule に渡して起動するのと同様, シェルスクリプトにも起動パラメータが渡せます.スクリプトの起動パラ メータは次のような特殊なシェル変数(スクリプトを実行すれば自動的に 設定される)として利用できます.   $argv 与えられたパラメータ全部(配列になっている)   $* $argv に同じ   $argv[n] n 番目のパラメータ, n は0以上    $argv[0] はコマンド(スクリプト)名自体   $n $argv[n] と同じ(ただし n > $#arg でもエラーにはならない)   $argv[m-n] m 番目から n 番目までのパラメータ   $#argv 与えられたパラメータの個数(コマンド名は除く)   $< 標準入力から文字列を1行入力する $0 あるいは $argv[0] を使うと,シェルスクリプトの名前を変えても,それに 応じた処理ができます. [例]2個のパラメータを与えて呼び出すスクリプトで,パラメータの個数が 足りないときには使い方を表示するようにしたい.ただし,コマンド名をあと で変更してもスクリプト自体は変えないようにしたい.このためには次のよう にすればよい. if ($#arg < 2) then echo "Usage: $0 file1 file2" exit(1) endif ... ... (処理の本体) $< という変数は標準入力から文字列を1行入力して格納するもので,これを 使うと,つぎのような対話的な処理ができます. [例] echo -n "Hello! My name is $1. What's your name?" set yourname=$< if ($yourname == "") then echo "Are you sick?" else echo "Hello, $yourname ! What a beautiful day, isn't it?" endif どのように動作するか実際にためしてみて下さい. 5).login, .cshrc, .logout のカスタマイズ Cシェルスクリプトの基本的な例は初期設定ファイル .login, .cshrc です. ここまでの説明からその内容はかなり理解できるはずです.ただし,慣れない うちはうっかり書き換えないように注意してください.シェルプログラミング の練習をある程度積んで,かつ,使用しているシステム固有の事情もある程度 理解した上で,自分流に書き換える(カスタマイズ)ことに挑戦して下さい. 書き換える場合には,かならずもとのファイルの写しを(例えば .login-old, .cshrc-old などの名前で)作って,いつでももとの状態を復元できるように してから行う,という慎重さが必要です.