(3) UNIX beginner? 〜C-Shellプログラミング〜
  

   シェルスクリプト
シェルのプログラミングは、ファイルからの入力で行う。コマンドラインからの入力と違い、いくつもの命令を一括して実行していく事ができるというのが利点である。この、いくつもののコマンドを手順に応じて編集したファイルをシェルスクリプトと言う。
シェルは他プログラミング言語と同様に、条件分岐、繰り返しといった制御命令も持っている。これらを用いる事で、複雑な手順を持ったシェルスクリプトを作成する事も可能である。





  

   シェルスクリプト作成
シェルスクリプトの文法は、コマンドラインからの入力のそれと基本的に同じである。大きな違いは、シェルスクリプトはまとめてテキストファイルとして記述し、実行する事ができるという点である。
それでは、カレンダーを表示するC-Shellスクリプトを作成してみる。

(viを用いて【List 1】を入力)


【List 1】
 1 
 2 
 3 
echo "===== カレンダー ====="
cal
echo "======================"






  
   シェルスクリプトを実行するには
シェルスクリプトをC-Shellにて実行する方法は2通りある。

 
(1)C-Shellにファイルを実行させる
csh ファイル名
ファイル名を引数としてC-Shellに直接渡してやることで、C-Shellはこのファイルを実行する。
これを、
sh ファイル名
とすれば、B-Shellにて実行することができる。
(今回の
【List 1】はC-ShellでもB-Shellでも理解できるスクリプトであったため、どちらのシェルでも実行可能である。ただしC-ShellとB-Shellには完全には互換性がない)

 
(2)ファイルに実行権を与える
chmod +x ファイル名
chmodでファイルに実行権を与えることで、それ以降、ファイル名を指定するだけで実行をさせる事が可能となる。ただし、この
【List 1】は、シェルスクリプト内でどのシェルを用いて実行するかを指定していなかったため、B-Shellにて実行される。
C-Shellで実行させたい時は、シェルスクリプトの行頭にC-Shellにて実行する旨を伝える一文をつけ加える必要がある
(C-Shellにて実行したい場合、行頭に追加する) #!/bin/csh





  
   コメントをつける
コメント・注釈をつけるには、「#」を用いる。「#」から文末までがコメント文であると解釈される。

echo "コメント文" #コメント文の実験





  
   メタキャラクタの打ち消し
特別な意味で解釈される文字をメタキャラクタという。そのメタキャラクタを通常の1文字として表示に使いたい場合、メタキャラクタの特別な意味を打ち消さなくてはならない。それには、以下の3種類の方法がある。

\(バックスラッシュ) この文字の後の1文字の特別な意味を打ち消す。
"(ダブルクォーテーション) 文字列単位で打ち消す。
但し"!$'の特別な意味は打ち消さない。
'(シングルクォーテーション) 文字列単位で打ち消す。
'!の特別な意味は打ち消さない。

#ちなみに日本語環境ではバックスラッシュは\を用いて表される。

 1 
 2 
 3 
例
echo Hello\!
echo "*** ABC \!a\$a ***"
echo '$super"'

結果
Hello!
*** ABC !a$a ***
$super

\などを取り除けば、それらが特別な意味を打ち消されていないことがわかるだろう。





  
   キーボードからの入力
引数の指定が面倒であるfindを、シェルスクリプトを用いて対話型スクリプトとして実行してみる。


【List 2】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
#!/bin/csh
echo -n "PATH を入力して下さい: "
set dir = $<		#キーボードからの入力待ちである
echo -n "ファイル名を入力して下さい: "
set name = $< 
echo "結果"
echo ""
find $dir -name "$name" -print

1行目:C-Shellで実行するよう指定する
2行目:-nオプションをつける事で、「PATH を入力して下さい」表示後に改行をしなくなる
3行目:set命令により代入を行う。「$<」はキーボードからの入力を意味する。 つまり、キーボードからの入力を変数dirに代入するということである
8行目:変数の頭に$をつける事で、その変数の値を利用している。





  
   配列について
配列にセットするには、以下のようにする。
set 変数名 = ( 値1 値2 値3 ・・・ 値n )
(値の区切りには半角スペースを用いる。半角スペースがデリミタ記号であると考えれば良いだろう)

 
値1を取り出すには、
$変数名[1]
とする。
(C言語とは違い、配列は1から始まる事に注意)
値2〜5を取り出すには
$変数名[2-5]
 
全ての値を取り出すには
$変数名 or $変数名[*]
とする。

 
配列の個数を調べるには
$#変数名





  
   引数の処理について
シェルスクリプトにおいても、起動時に変数を引数として渡す事が可能である。
与えられた引数は自動的にargvというシェル変数に代入される。このargvは通常のシェル変数と同じように使用する事が可能である。

スクリプト本体 $0 ($argv[0]という指定はできない)
引数の1番目 $1 or $argv[1]
引数の2番目 $2 or $argv[2]
引数の3番目 $3 or $argv[3]
引数のn番目 $n or $argv[n]
引数の個数 $#argv






  
   コマンド実行結果を利用する
コマンドを実行し、出力された文字を利用したい場合は、コマンドを「`」(=バッククォーテーション)で囲めばよい。


【List 3】
 1 
 2 
 3 
#!/bin/csh
set today=`date`
echo "現在の時刻は $today[3] です"

2行目:コマンドを「`」で囲むことでその結果を変数todayに代入することができる
3行目:変数todayの3番目のデリミタ値を文字列に組み込む





  
   結果をファイルに書き出す
ファイルに書き出すためには「>!」というオプションを利用する。echoやfindなど出力結果のあるコマンドであれば、全てファイルに書き出す事が可能である。
echo "test" >! "abc"

testをabcファイルに書き出す。abcファイルが存在していれば破棄。存在しなければ作成する。
これからわかるように、「>!」は強制的にファイルの上書きを行ってしまうので注意が必要である。ちなみに既存ファイルに追加したい場合は「>>!」を使う。

検索結果、検索した時間をファイルに書き出すように
【List 2】を作りなおしてみる。


【List 4】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
#!/bin/csh
set save_file = "result"
echo -n "PATH を入力して下さい: "
set dir = $< 
echo -n "ファイル名を入力して下さい:"
set name = $< 
echo "結果"
echo ""
find $dir -name "$name" -print  >! $save_file
echo "Date = 'date'" >>! $save_file
cat $save_file

2行目:変数save_fileに、保存するファイル名を入れる。もちろん、キーボードからの入力としてもよい。以後、保存ファイル名の指定は、変数save_fileを用いる。
9行目:findの結果をファイルに書き出す。
10行目:現在の日付をファイルに書き出す。「>>!」であるので、既存ファイルへの追加となる。
11行目:ファイルへ書きだした結果はディスプレーには表示されないため、catコマンドを用い、ファイルだけでなくディスプレーにも表示をするようにする。





  
   シェル変数を用いた計算
シェル変数に、計算した結果を代入したいといった場合は@を使用する。

0を代入 @ compute = 0
1を足す @ compute = $compute + 1
@ compute++
1を引く @ compute = $compute - 1
@ compute--
12を足す @ compute = $compute + 12
@ compute += 12
6を引く @ compute = $compute - 6
@ compute -= 6
4をかける @ compute = $compute * 4
@ compute *= 4
3で割る @ compute = $compute / 3
@ compute /= 3

固定数値や文字列を代入する場合はsetを用い、演算結果を代入する場合は@を用いるという風に使い分けなければならない。





  
   制御文
他のプログラミング言語と同じように、基本的な制御文がC-Shellにも用意されている。

条件文 (if-then-else-endif)
分岐文 (switch-case-endsw)
繰り返し文 (foreach,while,end)
ジャンプ文 (goto)
終了命令 (exit)






  
   【条件文】if
構文

if(条件式1) then
	コマンド
	コマンド
else if(条件式2) then
	コマンド
	コマンド
else
	コマンド
	コマンド
endif






  
   条件式
== 左辺と右辺の値が等しいとき全体として1(真)となる
!= 左辺と右辺の値が等しくないとき全体として1(真)となる
> 左辺の数値の方が大きければ全体として1(真)となる
< 左辺の数値の方が小さければ全体として1(真)となる
>= 左辺の数値が右辺の数値以上だったら全体として1(真)となる
<= 右辺の数値が左辺の数値以上だったら全体として1(真)となる


【List 5】
 1 
 2 
 3 
 4 
 5 
if( $answer == "yes" ) then
	echo "YES"
else
	echo "NO"
endif






  
   ファイル検査演算子
-r 指定されたファイルが読み込み可能であるとき真
-w 指定されたファイルが書き込み可能であるとき真
-x 指定されたファイルが実行可能であるとき真
-e 指定されたファイルが存在するとき真
-o 指定されたファイルの所有者であるとき真
-z 指定されたファイルのサイズが0であるとき真
-f 指定されたファイルが通常ファイルであるとき真
-d 指定されたファイルがディレクトリであるとき真


【List 6】
 1 
 2 
 3 
if( ! -d $dir ) then
	echo "$dir は存在しません"
endif






  
   論理演算子
式1 && 式2 式1かつ式2が真であるとき真
式1 || 式2 式1か式2のいずれかが真であれば真


【List 7】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
if( $ans1 == 0 && $ans2 == 0 ) then
	echo "BEST\!"
else if( $ans1 == 0 || $ans2 == 0 ) then
	echo "OK\!"
else
	echo "NO"
endif






  
   【分岐文】switch
構文

switch(変数)
	case パターン1:
		コマンド
		breaksw
	case パターン2:
		コマンド
		breaksw
	・・・
	・・・
	・・・
	case パターンn:
		コマンド
		breaksw
	default:
		コマンド
		breaksw
endsw





  
   パターンで指定できる文字
* 任意の文字列
? 任意の1文字
[ ] [ ]の中で指定された文字の中の任意の1文字


【List 8】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
switch($string)
	case[a]*:
		echo "OK"
		breaksw
	default:
		echo "NO"
		breaksw
8endsw






  
   【終了命令】exit
処理の途中で強制的にスクリプトを終了したい場合に使用する。


【List 9】(引数が無い時処理を終了する)
 1 
 2 
 3 
 4 
if( $#argv == 0 ) then
	echo "引数を指定して下さい。"
	exit
endif






  
   【ジャンプ命令】goto
特定の処理までジャンプさせる事ができる。ジャンプ先にラベルを指定しておき、ジャンプさせる場所にてそのジャンプ先ラベルを指定する。
他言語にも言える事だが、処理の流れがわかりづらくなるので、goto文の使用は控えた方が良い。


【List 10】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
#!/bin/csh
A:
echo "This"
goto C
B:
echo "a"
goto D
C:
echo "is"
goto B
D:
echo "pen."
exit






  
   【繰り返し文】while-end
同一の処理、もしくは類似した処理を、条件が満たされている間に、反復して処理したい場合にwhileを使用する。
構文

while(条件式)
	コマンド
end


条件式が真である間ループする。





  
   【配列の要素をずらす】shift
配列の要素を一つずつずらす命令である。shift命令を実行すると、1番目の要素がなくなり、2番目の要素が1番目、3番目の要素が2番目・・・となる。


【List 11】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
#!/bin/csh
set chars = ( a b c d e )
while( $#chars != 0 )
	echo $chars
	echo "----------"
	shift chars
end

結果
a b c d e
----------
b c d e
----------
c d e
----------
d e
----------
e
----------

参照する配列番号を変更するのではなく、配列自体に変更を加えている事に注意しなくてはならない。





  
   【繰り返し文】foreach-end
指定した配列の個数だけループを続ける。その時、指定した配列の値を順番に定義変数に代入していく。
構文

foreach 変数 ( 値1 値2 値3 ・・・ 値n )
	コマンド
end


ループはn回繰り返され、1回目のループでは、値1が変数に代入される。2回目は値2が、3回目は値3、n回目は値nが代入されるというわけである。
もちろん、指定する配列は変数であってもかまわない。例えば下のような例の場合である。


【List 12】
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
#!/bin/csh
set chars = ( a b c d e )
foreach test ( $chars )
	echo $test
	echo "-----"
end
echo $chars

結果
a
-----
b
-----
c
-----
d
-----
e
-----
a b c d e

ループする回数は5回。それぞれのループごとに順番に配列の値を代入している事がわかる。
こちらはshiftと違い、値が削除されていくということはない。



 


GO TO TOP!

[はいぱ〜みゅ〜じあむ] TOP PAGE!