シェルスクリプト

まず,作業用ディレクトリを作成して,そのディレクトリを作業ディレクトリにせよ.

mkdir myscript
cd myscript

シェル:
シェルとは,コマンド・インタープリタとも呼ばれ,UNIX系OSにおいてキーボードを介した
対話処理を実行するソフトウェアである.
その役割は,
	入力した文字列を「コマンド」と「オプション」と「引数」として解釈して,コマンドを実行
	コマンドが生成した標準出力を画面上に出力
となる.

UNIX系OSで利用できるシェルには歴史的には大きく分けて
	Bourne sh(B sh)とCsh
の2つの系統に分かれる.
Linuxで標準的に用いられているのは,Bourne shに準拠しながら大幅に機能拡張されたbashであり,
本学cc環境において使われているのはcshを機能拡張したtcshである.
ここでは,Bourne sh (bash)を想定したシェルスクリプトの作成を学ぶ.

ファイルパーミッション:
UNIX上で作成したファイルとディレクトリには,

	所有ユーザと所属グループ

の情報が付けられており,この情報に基づいてアクセス制御が行われる.
アクセス制御とは,ファイルやディレクトリに関して

	読み取り
	書き込み
	実行

に対して許可不許可を設定することである.
これらの情報を画面上で表示させるには,lsコマンドの-lオプションを用いる.

練習1:
ホームディレクトリにあるファイルやディレクトリについて,アクセス権の設定状態を各自調べてみよ.
過去のプログラミング実習で作成したC言語プログラムについて,
ソースプログラムと実行形式プログラムのアクセス権を調べよ.


各ファイルの所有ユーザを変更するには,chownコマンドを実行するが,
これはシステム管理者(root)でなければ実行できない.
この制限により,他のユーザ所有のファイルを勝手に所有権変更してイタズラできないようにしている.

各ファイルの所属グループを変更するには,chgrpコマンドで行う.
但し.これを実行しようとするユーザが複数のグループに所属するようにシステム管理者によって,
予め登録されていなければならない.グループ所属情報は/etc/groupに書き込まれている.
尚,cc環境では,このファイルの代わりにLDAPと呼ばれるシステム情報(ユーザ名や所属グループなど)を
ネットワークで共有する機構が併用されている.

ファイルのアクセス権を設定するのは,chmodコマンドを次のように使う.

	chmod アクセスモード 対象ファイル
	

アクセスモードの表現法には2つある.

シンボリックモード:
記号を用いて表現する.ファイルにアクセスできるユーザを

	所有者(owner): u
	グループ(group): g
	その他(others): o

で表現し,アクセス権を

	読み取り(read): r
	書き込み(write): w
	実行(eXecute): x

で表現する.
さらに,アクセス権を与える場合は + を用い, アクセス権を与えない場合は - で表す.
例えば,同じグループに所属するユーザに読み取り権限を与えない場合は

	g-r

と表現する.また,同じグループに所属するユーザとその他ユーザに書き込み権限を与えない場合は

	go-w

と表現する.

絶対モード:
アクセス権を3桁数字で表現する.
例えば,所有者・グループ・その他に読み出し・書き込み権を与え,実行権を与えない場合は

	666

と表す.この数字の計算の仕方は次の通り.

	読み出し権を与える場合: 4
	書き込み権を与える場合: 2
	実行権を与える場合: 1

これらの数字を所有者・グループ・その他ごとにたし算する.
例えば,読み出し・書き込み権を与え,実行権を与えない場合は,

	4 + 2 + 0 = 6

となり,読み出しと実行権のみ与える場合は,

 4 + 0 + 1 = 5 

と計算される.これを所有者・グループ・その他の順番で並べたものをchmodコマンドに与えて権限を設定する.
例えば,foo.sh に対して,所有者のみが読み込み・書き込み・実行が可能で,同じグループに所属するユーザ,
及び,全く無関係な一般ユーザに対しては,読み込みと実行を許可するには,

chmod 755 foo.sh

という具合に指定する.


練習2:
	date > sample.txt
を実行するとファイルsample.txtが作成される.

・このファイルの所有者とグループに書き込み権限が与えられないようにしてみよ.
もちろん,lsコマンドを用いてパーミッションが適切に設定されていることを確認せよ.

・その後,emacsで開いて編集(書き込み)できるかどうか確認せよ.(出来ないはず)

・所有者のみに書き込み権を与えるようにセットしてみよ.

・再度,emacsで開いてみて編集してみよ.(今度は編集可能となっているはずだ)


----------------------------------------------
シェルスクリプト(shell script):
一般にテキストファイルとして作成し,コンパイルせずに,そのまま解釈・実行される
仕組み(インタプリタ方式)で動作するプログラムを「スクリプト」と呼ぶことがある.

シェルスクリプトとは,シェルに与えることが出来る各種コマンドをテキスト
ファイルに書き並べて,解釈実行させるようにしたスクリプトである.
これは,ある一連のコマンド実行を自動的に行わせたり,
繰り返し実行させたりする目的で用いる.
既に用意されたUNIXコマンドを上手く組み合わせて,手軽に処理プログラムを
作成するために用いられる.


シェルスクリプトの基本:
ここではBourneシェル (Bシェル)を用いる.Bシェルスクリプトはemacsなどのテキストエディタで
作成すれば良い.
UNIX系OSの場合,ファイル名はどのように付けても特に支障が無い事が多いが,シェルスクリプト
であることが明確になるように .sh という拡張子を付けておくと良い.

Bシェルスクリプトの先頭行は

	#!/bin/sh

で始まることが約束である.これは/bin/shというプログラム(Bシェルのプロ
グラム本体)にスクリプトを読み込ませて,解釈・実行させることを意味する.

ちなみにCshスクリプトの場合は

	#!/bin/csh

と書かれる.

シェルスクリプトの書き方は,基本的には空白の開け方やインデントには影響を受けない
フリーフォーマットに近いが,若干の例外がある.
例えば,各コマンドは原則として一行に一つしか書けない.
複数のコマンドをどうしても書きたい場合は,セミコロン( ; )で区切るといった約束事がある.

スクリプトを作成したら,そのファイルに実行権を与えておくことが必要である.

練習3:ごく簡単なシェルスクリプトhello.shを作成して実行してみよ.

	#!/bin/sh
	
	## #記号以降行末まではコメントとして実行時には無視される
	echo "Hello World"
	date


シェル変数:
シェルスクリプト中では,変数(シェル変数)を用いることができる.
シェル変数は次の形で文字列や数値を代入できるが,文字列型や数値型といった変数型の区別は特にない.
また,変数名には大文字小文字の制限は特にないが,分かり易さのため大文字を使うことを勧める.

	V1="Hello"
	YEAR=2003

スクリプト中で変数の値を参照(使う)には,変数名の先頭に $ 記号を付ければ良い.
例えば,変数の値をechoコマンドで画面表示させたければ,

	echo $V1

	echo ${YEAR}

などと書く.

変数名は中カッコ {} で括ることがある.
この書き方は必ずしも必要ではないが,変数名を文字列としてしまうことを発見しやすくする
防ぐためのテクニックの一つである.
変数の先頭にあるべき$記号を付け忘れた場合,例えば,

	FOO="Hello"
	BAR=FOO   # FOOの先頭に$を忘れた!
	echo $BAR

としてしまった場合,これはエラーとならず,変数BARには文字列"FOO"が代入されてしまう.
しかし,

	FOO="Hello"
	BAR={FOO}   # FOOの先頭に$を忘れた!
	echo $BAR

としておけば,BARには"{FOO}"と代入されるので,より間違いを発見しやすくなる(かもしれない).


練習4:
上記で練習3で作成したシェルスクリプトのechoコマンドで表示されるメッセージを
変数V1に代入するようにしてみよ.

変数に入った値は連結して使う事ができる.それには変数を単に並べれば良い.
例えば変数VとWの値を連結して画面表示するには,

	echo ${V}${W}

とする.
結局,シェル変数は,それが出現する行が実行される時に,その変数の内容で置き換えられた状態で
コマンドが実行されると考えれば良い.

変数は「設定された状態」「設定されていない状態(存在しない事になっている状態)」がある.設定されていない状態にするには,
	unset 変数名
	例: unset YEAR
を実行する.


練習5:
以下のスクリプトworld.shを作成して実行してみよ.

	#!/bin/sh

	MSG="Hello "
	STR="World"
	
	echo ${MSG}${STR}
	
	MSG=""
	echo ${MSG}${STR}
	
	unset MSG
	echo ${MSG}${STR}


キーボードからの読み取り:
シェルスクリプトの実行時に標準入力から文字入力を読み取るには,readコマンドを用いる.
readコマンドは改行文字かEOFを検出するまでの文字を読み取って変数に代入する.
例えば変数 num に読み取るには,

	read num

を実行する.また,入力された文字列が

	one two three

といったように空白文字で区切られていた場合,

	read V1 V2 V3

となっていれば,変数V1, V2, V3にそれぞれ"one", "two", "three"が代入される.


練習6:以下のスクリプトkeyboard.shを作成して実行してみよ.

	#!/bin/sh

	read inkey
	echo -n "Your input: $inkey"
	read S1 S2
	echo "S1 ="$S1
	echo "S1 ="$S2

また,シェルスクリプトで参照する変数には,「環境変数(environment variable)」という種類もある.
これは,シェル(スクリプトを実行中のもの,キーボードからコマンドを受け付けているもの,両方含む)から,
実行した他のシェルスクリプトや通常のプログラムなどに受け継がれて参照される変数である.
C言語やJavaなどでプログラムを作成した場合でも,適当なライブラリを呼び出すことで
環境変数の値を読み取ることが出来る.ログイン時点で既にセット済みの環境変数にはHOMEやPATHといった
ものがある.セット済みの環境変数一覧はコマンド env で表示させることが出来る.

練習7:
次のコマンドをそれぞれ実行してみて,ターミナル上のシェルに定義済みの環境変数の内容を確認してみよ.

	echo ${HOME}
	echo ${USER}
	echo ${PATH}

練習8:
次のシェルスクリプトenv.shを作成・実行して環境変数の果たす役割を考えてみよ.

	#!/bin/sh
	
	echo ${HOME}
	echo ${USER}
	echo ${PATH}


環境変数を設定するには,まずシェル変数を設定し,そのシェル変数をexportコマンドで環境変数としてセットする.

例:
	FOO="bar"
	export FOO


シェルスクリプトは,起動された時に新しいシェルプログラムを一つ起動して,
その新しいシェルがスクリプト内容を実行する.
そのため,シェルスクリプト中でディレクトリを変更したりしても,
シェルスクリプトを起動したターミナル上のシェルは影響を受けない.

練習9:
次のコマンドを実行してみよ.

	echo ${PWD}

続いて,次のシェルスクリプトvariable.shを実行し,実行終了後,カレントディレクトリを確認してみよ.

	#!/bin/sh

	cd ${HOME}
	echo ${PWD}
	ls
	
	cd "/NF"
	echo "私は今どこにいるの??"
	echo ${PWD}
	ls


シェル引数変数:
シェルスクリプトを起動した際のコマンド名(スクリプト名)や引数は,スクリプト中で参照できる.
引数は $数字 という特別な名前を用いる.

練習10:
次のコマンドparamtest.shを作成せよ.

	#!/bin/sh

	echo "This is the script name: $0"
	echo "This is the first parameter: $1"
	echo "This is the second parameter: $2"
	echo "This is the third parameter: $3"


作成したコマンドを次のように実行してみて,

	./paramtest.sh One Two Three
	./paramtest.sh One Two Three Four
	./paramtest.sh One Two Three Four Five

各変数に代入されている値の状態を確認せよ.


その他の特殊変数
$#  スクリプトに渡された引数の数
$*  スクリプトに渡された引数を一行の文字列で表す.
$@  スクリプトに渡された引数を表す.引数区切りが保持される点が上と異なる.
$$  スクリプトの現在のプロセスID(実行中のプログラムに与えられる識別用通し番号)



各コマンドは原則として上から順番に実行されるが,一行には一つのコマンドだけが書ける.
一行に複数のコマンドを並べたい場合には,

	cd ; ls ; cd .. ; pwd

という具合いにセミコロンで区切る.これは次の書き方と同じ意味である.

	cd
	ls
	cd ..
	pwd


パイプラインとリダイレクション:
スクリプト中で起動したコマンドの処理結果は,基本的にはそのまま標準出力(通常は画面表示)へ出力される.
しかし,対話シェルと同様にコマンド実行結果のパイプライン処理やリダイレクションを使うことができる.
例えば,

	ls | sort -r 

とすればファイル名のアルファベット逆順が画面表示されるし,

	date > foo.txt

で,日付の文字列がfoo.txtファイルに保存される.また,

	date >> bar.txt

とした場合は,bar.txtへ追加書き込みされる.
また,コマンドへの標準入力をファイルに接続して,ファイルから読み取るようにするには

	sort < sample.dat

とすれば良い.この場合,sample.datファイルの内容がsortコマンドの入力となる.


練習:
次のようなテキストファイルnumber.datを作成せよ.

	one 1
	nine 9
	three 3
	six 6
	eight 8
	seven 7
	five 5
	two 2
	four 4

・このnumbet.datを読み込んで,並べ替え(sortコマンドを使う)を行った結果を
標準出力へ出力シェルスクリプトを作成せよ.それは正しく動作しているか?

・スクリプトの起動の仕方を工夫して、結果をresult.datというファイルへ
リダイレクトして保存してみよ.

・並べ替え処理結果は必ずresult.datへ保存されるスクリプトとなるように修正し,
それが正しく実行されることを確認せよ.


課題その1:
最初の引数に入力ファイル,2つ目の引数に出力ファイルを指定すると,
指定された入力ファイルを読み込んで,そのうちアルファベット e が出現す
る行のみを抜き出し(grepコマンドを使う),それをアルファベット逆順に並べ替えた結果を
指定された出力ファイルに保存するようなスクリプト reverse1.sh を作成せよ.
このスクリプトが上記のnumber.datに対して正常に動作することを確認すること。
実行例:
	reverse1.sh number.dat output.dat
(output.datというファイルに結果が保存されること)


課題その2:
実行すると以下の実行例のように入力ファイルと出力ファイルの名前(パス名)を問い合わせてきて,
応答したファイルに対して,上記の課題その1と同様の処理を行うスクリプト reverse2.sh を作成せよ.

実行例:
Input File: foo.txt
Output File: bar.txt


ファイル名の置換:
シェルでは次の特殊文字(メタキャラクタ)を用いて,ファイル名にマッチするパターンを表現できる.
	*	空文字列を含む任意の文字列にマッチ
	?	任意の1文字にマッチ
	[...]	カッコで囲まれた内のいずれかにマッチ
	[!...]	カッコで囲まれた文字以外の任意の1文字にマッチ

練習:次のコマンドを実行してみよ.
	ls *
	ls *.dat
	ls [lmn]*

このメタキャラクタを上手に使うと,特定の約束事に基づいて名前が付けられているファイルだけを
引数に指定することが簡単に出来る.例えば,次のような例では,どのような名前が付いている
ファイルが削除されるか考えてみよ.

	  rm *.c
	  rm a*.jpg
	  rm [ps]*
	  rm ?x* 



引用符:
シェルスクリプトでは,引用符は特別な意味合いを持つので注意が必要である.

二重引用符: 通常よく使われる引用符.ファイル名パターンに使われる特殊文字の特別の意味は
失われるが,ドル記号($),バッククォート(逆引用符 ` ) ,バックスラッシュ(¥)は,
特別の意味を失わないので,変数参照することが可能.

	例:	"*** Here, we go ***"

一重引用符:最も強力な引用符. バックスラッシュ以外の特殊文字の特別の意味は失われる.

	例:	'*** $0 is $0 $"***'

逆引用符:引用符で囲まれた部分をコマンドとして実行した結果の文字列で置き換えられる.

	例:	DATE=`date`

この場合、変数DATEにはdateコマンドを実行した結果,つまり,その瞬間の時刻の文字列が代入される。

練習:以下のシェルスクリプトを実行して,引用符の意味を考えてみよ
(echo -n とは改行しないメッセージ出力).

	#!/bin/sh

	STR="Hello World"
	TODAY=`date`

	echo "The first message: ${STR}"
	echo 'The second message: ${STR}'
	echo "The third message: '${STR}'....why?"
	echo -n "Today is "; echo ${TODAY}
	echo "You have files: "`ls`


ヒアドキュメント(here document):
スクリプト中で,スクリプトに直接書かれた文字列をコマンドへの入力として与える方法がヒアドキュメントとよばれる.
例えば,画面に次のようなメッセージを出す場合,

	Welcome to Linux world
	----------------------
	Please select your choice:

		1. デフォルトインストール
		2. カスタムインストール
		3. 終了

echoコマンドでは,

	echo "Welcome to Linux world"
	echo "----------------------"
	echo "Please select your choice:"
	echo 
	echo "1. デフォルトインストール"
	echo "2. カスタムインストール"
	echo "3. 終了"

などと,かなり面倒な記述になるが,ヒアドキュメントを用いるとcatコマンドを用いて

	cat <<ENDOFMSG
	Welcome to Linux world
	----------------------
	Please select your choice:

		1. デフォルトインストール
		2. カスタムインストール
		3. 終了
	ENDOFMSG

と書ける.ここではcatコマンドのへの入力として cat <<ENDOFMSG から ENDOFMSG までで
囲まれた文字列が与えられている.ヒアドキュメントの書式は次の通り.

	コマンド <<KEYWORD
	コマンドへの入力文字列
	コマンドへの入力文字列
	  .......
	KEYWORD

KEYWORDは,入力文字列に出現しない文字列であれば,何でも構わない.


条件式:
シェルスクリプト中で,真偽の条件判断を行うには,2つの方法がある.
  test 条件
又は
  [ 条件 ] 
である.いずれを用いても良いが,[ ]の前後には空白を必ず入れる.

条件に使える記法はかなり多いので詳細は適当な参考書など(例えばここのサイトなど)を調べること.

ファイル関連の条件
	-r file		fileが存在して読み取り可能
	-w file		fileが存在して書き込み可能
	-x file		fileが存在して実行可能
	-f file		fileが存在して,それは通常のファイルである
	-d file		fileが存在して,それはディレクトリである
	-s file		fileが存在してサイズが0より大きい
	-L file		fileはシンボリックリンク

文字列比較の条件
	s1		s1がヌル文字列(空文字列)ではない
	s1 = s2		s1とs2は等しい
	s1 != s2	等しくない
	-z s1		s1はヌル文字列である
	-n s1		s1はヌル文字列でない

数値比較の条件
	n1 -eq n2	n1とn2は等しい
	n1 -ne n2	等しくない
	n1 -gt n2	>
	n1 -ge n2	>=
	n1 -lt n2	<
	n1 -le		<=

また,条件の否定を表すときは,

[ ! 条件 ]

という具合に "!"記号を条件の前に付ける.	


コマンド終了ステータス:
シェル中で起動したコマンドが終了した時に,各コマンドは終了ステータス(整数)を返す.
直前に実行されたコマンドのステータスは変数 $? で知ることが出来る.
一般に,正常終了した場合は0が終了ステータスとなる.
この終了ステータスも条件式として利用できる.


数値演算:
Bourneシェルでは,数値演算を行うためにはexpr コマンドを使わねばならない.

使い方の典型例:
	VALUE=`expr $V1 + $V2`

exprコマンドは引数で与えられた文字列を数式として計算(評価)した結果を返すコマンドである.
注意するべき点はかけ算を行う場合にはバックスラッシュ文字でエスケープしなければならない事である.

	V3=`expr $V1 ¥* $V2`

このように,バックスラッシュにはシェルスクリプト中で特殊文字の特別の意味を打ち消す働きがある.

	
制御構造:
シェルスクリプトにも,通常のプログラミング言語と同様の制御構造が存在する.
その制御する条件記述には上記の条件判断の記法を用いる.

条件分岐:
ある条件を満たす場合に処理を実行するものとしてif-then文がある.その構文の形は次の通り.

	if [ 条件式 ] ; then
		条件が真の場合に実行する内容
	fi

又は

	if [ 条件式 ]
	then
		条件が真の場合に実行する内容
	fi

さらに,条件が真の場合と偽の場合で処理を切り替えるには,if-then-else文を用いる.その形式は次の通り.

	if [ 条件式 ] ; then
		条件が真の場合に実行する内容
	else
		条件が偽の場合に実行する内容
	fi

又は

	if [ 条件式 ]
	then
		条件が真の場合に実行する内容
	else
		条件が偽の場合に実行する内容
	fi


例:
 if [ $STR = "Java" ] ; then
		 echo '$STR is Java'
	else
		 echo '$STR is not Java"
	fi


case文による条件分岐:
C言語と同様に,複数の選択肢から一つを選んで実行する構文として,case文がある.構文は次の通り.

	case value in
	pattern1)
		....
		;;
	pattern2)
		....
		;;
	esac

また,patternの部分にはファイル名の場合と同様のメタキャラクタを使うことが出来る.
また "|"を使って,パターンの「又は(論理和)」を表現できる.
但し,C言語と違って,パターンにマッチしてコマンドが実行された後はcase文を終了して,
esac以下の文に制御が移る.

練習:以下のスクリプトを実行し,表示される問い合わせに適当に返答してみよ.

	#!/bin/sh

	echo "あなたは学生ですか"

	read key

	case $key in

	y|Y|yes|y*)
		echo "あなたは学生ですね"
			;;
	n|N|no|n*)
		echo "あなたは学生ではありません"
			;;
	*)
		echo "は? なんですか?"
		 ;;
	esac

 echo "Bye Bye!"



繰り返し処理:
ある条件が満たされている間,処理を繰り返すための構文としてwhile文がある.その形式は次の通り.

	while コマンド
	do
		繰り返し内容
	done

コマンドには [ 条件式 ] の形を使うことが出来る.
この構文は前判定なので,条件式が最初から満たされれば繰り返し内容が一度も実行されないことがある.
それに対し,条件が満たされるまで実行を繰り返す(つまり,条件が偽ならば繰り返される)構文として
until構文がある.これも前判定であるので,繰り返し内容が一度も実行されないことがある.

	until コマンド
	do
		繰り返し内容
	done

さらに,繰り返し処理を
 打ち切るコマンドは break 
 直ちに次の繰り返しを開始するコマンドは continue 
である.


練習:以下のスクリプトを実行してみよ.

	#!/bin/sh
	# number.datファイルから入力データを読み取って画面表示する.

	echo "while文のテスト"
	while read num word
	do
		echo -n "num = "; echo ${num}
		echo -n "word = "; echo ${word}
	done < number.dat   # この部分がwhile文へのリダイレクト入力を意味する.

同じ処理の別な例:

	#!/bin/sh
	# number.datファイルから入力データを読み取って画面表示する.

	echo "while文のテスト"

	# catコマンドは引数で与えられたファイル内容をそのまま標準出力へ送り出す.
	cat number.dat | while read num word
	do
		echo -n "num = "; echo ${num}
		echo -n "word = "; echo ${word}
	done

 forループ:
繰り返し構文として、for文も利用できる。構文は以下の通り.

	for Variable in list
	do
	    繰り返し内容
	    繰り返し内容
	    .......
	done

使用例:
	#!/bin/sh
	# 数字を1から5までを順番に表示

	LIST="1 2 3 4 5"
	for IDX in ${LIST}
	do
		echo ${IDX}
	done

別の使用例:
	#!/bin/sh
	# ファイル内容を順番に一つのファイルに連結"

	FILES="foo.dat bar.dat hoge.dat hogehoge.dat"

	for IDX in ${FILES}
	do
		cat ${IDX} >> /tmp/result.dat
	done


シェルスクリプトにおける関数(function)定義:
あまり長大なスクリプトを書き並べると,全体の処理の流れを把握することが難しくなる.
C言語では,長くなってしまう処理を「関数」として定義することで処理内容見通しを良くすることが出来た.
シェルスクリプトにおいても関数(function)を定義することができるので,これを上手く使えば,
ある程度分かり易いスクリプトとすることが出来る.
関数を定義する場合は,

foo() {

}

という形の関数定義をスクリプトの先頭部分で定義しておく必要がある(つまり,関数は,使う前に
定義しておかないとエラーになる).例えば,次のように使うことが出来る.

##!/bin/sh
# functionの使い方

# 関数hogeの定義
hoge() {
	echo "Here is inside of hoge."
	echo "BAR = ${BAR}"
	BAR="HOGE"
}

# 関数chomeの定義
chome() {
	echo "This is chome function."
	echo "BAR = ${BAR}"
}

# スクリプト本体処理の開始
BAR="Before hoge"

hoge
chome

exit 0

但し,C言語とは異なり,シェル変数は全ての関数で値が共有される(大域変数)ことに注意が必要
である(上記のスクリプトを動かしてみて,変数BARの内容がどうなっているか理解せよ).
また,関数内部で実行したコマンドの標準出力は,まるで関数が普通のコマンドであるかのように
標準出力へ出力されるので,次のような使い方も出来る.

#!/bin/sh

mycommand() {
	cat number.dat | grep "e"
}

# スクリプト本体開始
mycommand | sort

exit 0

このスクリプトの処理結果は, cat number.dat | grep "e" | sort を実行したものと同じになる.

課題その3:入力された回数に応じてKSUという文字をピラミッド型に表示するシェルスクリプト ksu.shを作成せよ.

実行例:
	回数を入力して下さい > 3
	KSU 
	KSU KSU 
	KSU KSU KSU 

	実行例:
	回数を入力して下さい > 5
	KSU 
	KSU KSU 
	KSU KSU KSU 
	KSU KSU KSU KSU 
	KSU KSU KSU KSU KSU


課題その4:テキストファイルを標準入力から読み取り,先頭の文字が#記号で始まる行だけを
取り除いて標準出力へ出力するスクリプト comment-filter.sh を作成せよ.但し,フィルタコマンドとして利用できること.
(ヒント:read文とcase文とメタキャラクタを上手く組み合わせて使うべし)
尚,フィルタとして動作せねばならないから,例えば,次のようにコマンドとして利用できることが必須である.
cat sample.txt | ./comment-filter.sh > /tmp/output.txt


課題その5:
ディレクトリ
	/NF/class0/oomoto/applied/image_fragments/
には,かなり数多くのファイルが配置されている,これらのファイルは,次の条件を満たしている.

・各ファイルの中身はテキスト(文字列)である.

・拡張子.imgを持つファイルは,各ファイル名の拡張子を取り除くと5文字以下の
アルファベットになっている.
これらのファイルの中身をファイル名の降順(アルファベット逆順)で取り出して,
一つのテキストファイルとして連結するとppm形式の画像データファイルとなる.
この画像データを最終的にPNG形式ファイルSNOW.pngとして取り出されねばならない.

・拡張子.datを持つファイルは,各ファイル名の拡張子を取り除くと4桁以下の整数になっている.
これらのファイルの中身をファイル名に含まれる整数値の降順で取り出して,
一つのテキストファイルとして連結するとppm形式の画像データファイルとなる.
この画像データは最終的にJPEG形式ファイルKSU.jpgとして取り出されねばならない.

・拡張子.srcを持つファイルは,各ファイル名の拡張子を取り除くと2桁以下の数字になっている.
これらのファイルの中身をファイル名の数字の降順で取り出して,一つのテキストファイルとして
連結するとXeasyGraphicライブラリを利用したC言語ソースプログラムになる.
このソースプログラムを自動的にコンパイルし,最終的に実行形式プログラムが生成されねばならない.

このように,細かく細切れ,且つ,ごちゃまぜになったファイル群から必要なファイルを自動的に復元し,
最終的に生成された「JPEG画像データやPNG画像データや実行形式プログラムだけ」をホームディレクトリに
保存する.
さらに,画像については自動的に画面表示し,また,実行形式プログラムは自動起動してグラフィックスを
表示させるまでを実行するシェルスクリプトを作成せよ.
但し,次の事項に注意すること.

・XeasyGraphicをつかったC言語プログラムのコンパイルはeasyccコマンドを使う.

・最終的に生成されるJPEGやPNG画像ファイルや実行形式ファイルを除いて,スクリプト実行中に一時的に
作成されるファイルがある場合には,スクリプト終了時に全て残らず自動的に削除して掃除されるようにし,
後にゴミファイルを残さない配慮をしておくこと.
この課題の画像データはPPM形式で保存すると15MB位になるので,ホームディレクトリ以下には長期保存しないよう注意するべし.
あっという間にcc環境のファイル容量制限を使い潰して一切作業出来なくなる危険性がある.
一時的にファイルを置くための作業用領域が必要であれば /tmpディレクトリを利用せよ.

・image_fragmentsディレクトリには,通常のテキストファイル以外に「ディレクトリ」も配置されている.

・PPM形式からJPEGやPNG形式に変換するには,pnmtojpegコマンドやpnmtopngコマンドで行える.
これらは全て,標準入力からデータを受け取って標準出力に出力するフィルタプログラムである.
例えばtemp.ppmファイルからJPEG形式ファイルresult.jpgを生成するには,次のようにすれば良い.

使用例:  cat temp.ppm | pnmtojpeg > result.jpg 

課題実行例:

	希望する処理を選択してください.
	1. 画像データSNOWの表示
	2. 画像データKSUの表示
	3. グラフィックスプログラムの実行
	x. 終了
	Choice >> 
(ここで,適当な整数,又は,アルファベットxを入力すると,適当なグラフィックスが表示される,或いは,終了する.)

注意事項: 提出するスクリプトは「誰が起動しても」正常に動作するように作成せよ.
具体的には,スクリプト中で「ホームディレクトリ」を絶対パスで書いた場合,例えば,
 例: foo_comm | bar_comm > /NF/home/g020/somebody/output.dat
といった書き方をしている場合,このスクリプトは誰が起動しようとも,その絶対パスで指示されたファイルへ
書き込みしようとするから,そのユーザ以外はエラーとなって実行できない.
これを避けるには,ホームディレクトリは環境変数 $HOME で参照できることを利用して,
  foo_comm | bar_comm > ${HOME}/output.dat
とするように配慮すれば,スクリプトを起動したユーザのホームディレクトリにファイルが
書き込まれるから,誰がそのスクリプトを起動しても正しく動作するだろう.

補足: grepコマンドでピリオド"."を含んだ行を取り出すために,引数の文字列として,例えば,".img"のようなものを渡した場合,
ピリオドは特別の意味を持っているために,次の処理

	cat foo.txt | grep ".img"

は期待した動作をしないことがある.その場合,その特別の意味を打ち消すために,

 cat foo.txt | grep "¥.img" 

として,バックスラッシュをピリオドの前に置いてやると意図通りになるかもしれない
あるいは,シングルクオートで囲むようにすればピリオドの特別な意味は確実に打ち消される.


課題その6:
	/NF/class0/oomoto/applied/tryit
を実行すると,ホームディレクトリにfile_adventureディレクトリが作成される.
これは,一年生時に実習したファイル操作練習の素材であり,‾/file_adventure/readmeファイルを
出発点にして,ファイルやディレクトリ操作をキーボードから操作しながらキーワードを探してゆく
ゲーム形式の課題になっている.
ここで,/NF/class0/oomoto/applied/tryitを実行するところを出発点に,自動的にファイル操作を行いながら,キーワードが含まれたテキストファイルを順番に自動表示させるシェルスクリプトを作成せよ.但し,キーワードを全て集め終わった時点でスクリプトは終了し,キーワードのメール送信は行わないものとする.
そのシェルスクリプトの先頭部分は次のようになるであろう.

#!/bin/sh

echo "123456 産大太郎"
echo "File Adventureの自動実行スクリプト"

/NF/class0/oomoto/applied/tryit

# まずホームディレクトリにあるfile_adventureディレクトリに移動
# ホームディレクトリは環境変数${HOME}を使って表す.
cd ${HOME}/file_adventure

# そこにあるreadmeファイルを画面表示
cat readme

# 指示に従って,次の操作を行う.
cd ....

# 以下,省略

尚,ファイル操作はスクリプト中で自動的に行い,「ディレクトリにファイルはいくつありますか?
 その数と同じ名前のファイルを見なさい」などという指示になっている場合は,
スクリプト中でファイル数を自動的に数えて(ls some_dir | wc -l),
読むべきファイルを自動的に選択して表示するように工夫すること.
つまり,普通にキーボードを操作して,一つ一つの指示に従って
file_adventureをこなした後の状態と全く同じディレクトリやファイルの状態が
自動的に残るスクリプトとしなければならない.



課題提出要領:

レポートシステムを用いて提出せよ.レポートシステムの使い方は以下のページを参照されたし.

	http://www.ics.kyoto-su.ac.jp/‾oomoto/lecture/program/C/report_system.html

添付ファイルによる提出は認めないから注意すること.
各スクリプトの2〜3行目付近に各自の学籍番号と氏名をコメント行として書いておくこと.
コメントの先頭は # 記号で始まることを忘れないこと!
尚,提出されたレポートの採点にあたっては,シェルスクリプトを用いて自動的な動作チェックを
施すので,くれぐれも「誰が起動しても実行例通りに正しく動作する」ものを提出すること.


課題その1について:
レポート名: oomoto/applied/shell/report1

課題その2について:  
レポート名: oomoto/applied/shell/report2 

課題その3について:  
レポート名: oomoto/applied/shell/report3 

課題その4について:
レポート名: oomoto/applied/shell/report4

課題その5について:
レポート名: oomoto/applied/shell/report5

課題その6について:
レポート名: oomoto/applied/shell/report6

締め切り:4/18(土)

大本英徹
工学部情報通信工学科