オペレーティングシステム2

情報工学科 3年生対象 専門科目
システムプログラミング
第3回、第4回、第5回、第6回
makeコマンド
動的リンクライブラリ
シェルスクリプト
情報工学科
篠埜 功
開発支援ツール make
• コンパイルを要するソースファイルの数が多くなった場合,手
間がかかる
• 1つのヘッダファイルを複数のソースファイルで使用している
場合,ヘッダファイルの修正→ 関連する全てのソースファイ
ルの際コンパイル
• makeを使用
– ファイルの変更をした場合、その影響を受けるファイルを
再コンパイルする。(不要な再コンパイルをしない)
– ファイルの日付情報を用いる。
Makefile
• ファイルの作成方法を記述したルール
– コロンの前にmakeで作成するファイル名前を書く。
– コロンの右にコロンの左のファイルを作るために必要なファイルをス
ペースで区切って並べる
– 次の行以下にそのファイルを作るためのコマンドをTABキーを押し
たあとに記述。2つ以上のコマンドを書いてもよい。
(例)
TAB
main : main.o add.o mult.o
gcc -o main main.o add.o mult.o
main.o : main.c addmult.h
gcc -c main.c
add.o : add.c
gcc -c add.c
mult.o : mult.c
gcc -c mult.c
make
• さきほどの例を内容とするMakefileというファイルを作成し、
$ make
を実行する。この場合、Makefileの一番上のmainを作成しようとする。
main : main.o add.o mult.o
gcc -o main main.o add.o mult.o
main.o : main.c addmult.h
gcc -c main.c
add.o : add.c
gcc -c add.c
mult.o : mult.c
gcc -c mult.c
make
さきほどの例で、add.cやaddmult.hなど、ソースファイルの
内容を変更し(スペースやコメントを入れるなど)、
$ make
を実行して何が起きるか確認せよ。
(参考) touchコマンドを使うと、中身を変えずにファイルの
時刻情報のみ更新できる。
$ touch add.c
など。
make
$ make add.o
などと、コロンの左側のものをmakeの引数に与えると、それ
を作成しようとする。
main : main.o add.o mult.o
gcc -o main main.o add.o mult.o
main.o : main.c addmult.h
gcc -c main.c
add.o : add.c
gcc -c add.c
mult.o : mult.c
gcc -c mult.c
Makefile
• コロンの左に書くのはファイル名でなくてもよい。
test1:
ls –l
test2:
ps –ef | grep sasano
$ make test1
とするとファイル一覧が表示され、
$ make test2
とするとプロセス一覧からsasanoを含む行を抜き出し
たものが表示される。
make
clean というターゲットを作っておき、実行コマンドとして不要な
ファイルを削除するコマンドを書いておくということがよく行われる。
$ make clean
とすると、ソースファイルではないファイルが削除されてディレクト
リがきれいになる。
main : main.o add.o mult.o
gcc -o main main.o add.o mult.o
main.o : main.c addmult.h
gcc -c main.c
rm に-fオプションは
add.o : add.c
強制削除のオプ
gcc -c add.c
ションであり、存在
しないファイルが引
mult.o : mult.c
数にあたえられても
gcc -c mult.c
メッセージが出ない。
clean :
rm –f main main.o add.o mult.o
Makefileの名前について
Makefileの名前は”Makefile”でなくてよい。
makeコマンド(GNU make)はdefaultでは”GNUmakefile”,
“makefile”, “Makefile”をこの順で探し、最初に見つかった
もので実行する。これ以外のものを使いたい場合は-fオプ
ションで指定する。たとえばmyMakefileという名前で
Makefileを作成したときは、
$ make –f myMakefile
のように実行すればよい。
練習問題
前回のlibaddmult.aを作ってからmainを作成する手順を
makeコマンドで行えるようにMakefileを作成せよ。
マニュアル表示コマンド man
• (例) gcc, ar, makeコマンドについて調べたい
$ man gcc
$ man ar
$ man make
• コマンドの処理内容,様々なオプションの解説が書か
れている。
• manコマンドについては
$ man man
で調べる。
共有ライブラリ(shared library)
• 動的リンクライブラリ(dynamic link library)とも言う。
• 実行形式ファイルにはライブラリの中身は含まれず、
実行時にリンクされる。
• 実行形式ファイルのサイズの削減
• ライブラリを修正する場合、ライブラリファイルのみ修
正すればよく、リンク作業が不要。
共有ライブラリ作成例
さきほどのadd, mult関数の例で共有ライブラリを作成する。
$ gcc –shared –o libaddmult.so add.c mult.c
これでadd, mult関数が共有ライブラリとしてlibaddmult.soに作成さ
れる。
$ gcc –L. –o main main.c –laddmult
(あるいは $ gcc –o main main.c libaddmult.so と明示的に指定)
これでlibaddmult.soが実行時にリンクされるようになる。
$ ./main
を実行する前に、
$ setenv LD_LIBRARY_PATH .
を実行しておく。(シェルがtcshの場合。シェルがbashの場合は、
$ LD_LIBRARY_PATH=.; export LD_LIBRARY_PATH
とする。) add.cを変更してlibaddmult.soを作り直し、mainを実行する
と、mainを作成しなおさなくても変更が反映されていることが分かる。
$ ldd main
(lddはlist dynamic depencenciesの略)
で、mainが動的にリンクするファイル一覧が表示される。
補足
• -lxxx でリンクする際には、libxxx.soが先に検
索される。
(例)/usr/lib/libm.so, /usr/lib/libm.aのように
両方ある場合は/usr/lib/libm.soが(defaultで)
使われる。
レポート課題1
1.
2.
3.
4.
5.
6.
長さnのint型の配列の各要素に1から100までの整数をランダムに作成して
代入する関数randAssignを定義したファイルrandAssign.c
長さnのint型の配列を受け取り、要素を小さい順に並べ替える関数sortを定
義したファイルsort.c
randAssign関数のプロトタイプ宣言を記述したファイルrandAssign.h
sort関数のプロトタイプ宣言を記述したファイルsort.h
以下の内容のmain関数を記述したmain.c
–
長さnの配列を作成し、それをrandAssign関数に渡して整数をランダムに
格納させ、sort関数に渡してソートをさせ、その結果を画面に出力するプ
ログラム。長さnの値はmain.cに直接書いてよい。キーボードから入力す
るようにしてもよい。
–
main関数の先頭部分でrandAssign.h, sort.hを読み込む。
Makefile
–
実行形式ファイル(mainとする)をmakeコマンドで作成できるように作成す
る。
配列を関数に渡すとき、長さをもう一つの引数として
渡す。
レポートの提出方法
□ 下記のファイルを作成し、提出
• randAssign.c, randAssign.h, sort.c, sort.h, main.c,
Makefile, kadai1.txt
□ 提出方法
システムプログラミング講義用の課題提出用フォルダ内に
あるkadai1というフォルダの中に自分の学籍番号を名前と
するフォルダを作成し、その中に上記ファイルを置く。
kadai1.txt内に学籍番号、氏名、日付、および作成したプ
ログラムの簡単な説明を記載する。
□ 提出期限
10月27日の講義開始時間まで。締め切り後に提出した場
合、成績への反映を保証しない。
□ アーカイブを作る必要はありません(作ってもいいですが)。
□ 動的リンクにするか静的リンクにするかについても自由。
前回の補足
共有ライブラリを作る時、gccのオプションで-fPICをつけた
方がよい。
(例)
$ gcc –fPIC -shared –o libaddmult.so add.c mult.c
その他の部分は同じ。
シェルスクリプト
• シェルに対する命令をファイルに記述したもの
• シェルとは
– コマンドインタプリタ。UNIX系OSではシェルはユ
ーザプログラムであり、自分でシェルを作成する
こともできる。
シェル(/bin/sh)に直接打ち込む例
[sasano@oli004 ~]$ sh
sh-2.05b$ A=ls
sh-2.05b$ $A
<lsの実行結果>
sh-2.05b$
シェルスクリプトは、シェル(通常
は/bin/sh)に対する命令列をファ
イルに格納したものである。ここ
ではファイルに入れずにシェル
(/bin/sh)上で直接実行してみる。
(補足) shと打つことによって、shというファイルが環境変数
PATHに入っているディレクトリから検索され、その結果
/bin/shが見つかり、それが実行される。
A=ls などのように、変数名=文字列
の形で、変数を定義できる。=の前後にスペースを入れて
はいけない。スペースを入れると、この場合、Aをコマンド
名として解釈しようとしてcommand not foundになる。
変数の値の参照は、$変数名とすればよい。
(参考) /bin/tcshの場合
[sasano@oli004 4kai]$ set A=ls
[sasano@oli004 4kai]$ $A
<lsの実行結果が表示される>
[sasano@oli004 4kai]$
/bin/tcsh では、上記のように、set A=lsの形で、set
コマンドを用いてシェル変数を定義する。(ここでは=
の前後にスペースがあってもよい。)
シェル変数の一覧は
$ set
で画面に表示される。(shでもsetで一覧表示。)
環境変数
/bin/shや/bin/bashの場合:
$ export シェル変数名
のようにすることにより、シェル変数が同一の変数名で環境変数となる。
たとえば、
$ A=test
$ export A
など。環境変数の一覧は
$ printenv
で表示される。個々の環境変数の値は
$ printenv A
のように、環境変数名をprintenvコマンドの引数に与えると表示される。
/bin/tcshの場合:
$ setenv A test
のように、setenvコマンドを用いる。(=はないことに注意)
環境変数一覧、個々の値の表示については/bin/shと同じ。環境変数の
値も$環境変数名で参照できるが、同じ名前のシェル変数がある場合、
そちらが優先される。
環境変数の例
dateコマンド(日時の表示)を実行すると
[sasano@oli004 4kai]$ date
2009年 10月 6日 火曜日 13:45:42 JST
のようになるが、
[sasano@oli004 4kai]$ setenv LANG C
のようにすると、
[sasano@oli004 4kai]$ date
Tue Oct 6 13:47:27 JST 2009
のように英語表示になる。
(/bin/shの場合は、LANG=Cとしてからdateを実行
すればよい)
シェル変数の例
PATH, HOME, USER, HOSTNAMEなどのシェル変数が
通常使われている。
$ echo $PATH
$ echo $HOME
$ echo $USER
$ echo $HOSTNAME
などで確認できる。
/bin/shでも/bin/tcshでも同じ。
シェルスクリプト
• ファイルにシェルに対する命令(スクリプト)を書いた
もの。
• 1行目に#!/bin/sh と書く。これによって、/bin/shがス
クリプトを実行することになる。
– #!/bin/bash, #!/bin/tcshなど、他のシェルを指定し
てもよいが、その場合はスクリプトの書き方は異な
る。
• ファイルの属性を、実行を許可するように変更する必
要がある。
$ chmod 755 test.sh
などのようにして変更できる。
例1(打ち込んで確認)
(1)以下を中身とするtest1.shというファイルを作成
#!/bin/sh
ls -l
(2)ファイルの属性を変更
$ chmod 755 test1.sh
(3)実行
$ ./test1.sh
これによって、ls –lが実行される。あるいは、
$ /bin/sh test1.sh
でもよい。
(4) カレントディレクトリのファイルリストが表示されることを
確認。
シェル変数の使用(打ち込んで確認)
(1)以下を中身とするtest2.shというファイルを作成
#!/bin/sh
A=ls
B=-l
$A $B
(2)ファイルの属性を変更
$ chmod 755 test2.sh
(3) 実行
$ ./test2.sh
(4) カレントディレクトリのファイルリストが表示されるこ
とを確認。(例1と同じ)
for文(例1)(打ち込んで確認)
#!/bin/sh
for i in 1 2 4
do
echo $i
done
1
2
4
と表示されればOK。
echoは引数に与えられた文字列
を表示するコマンド。$iが文字列
に展開されてから表示される。
for文の構文、意味
構文
for variable in wordlists; do commands; done
wordlists: 要素をスペースかタブで区切る。
commands: コマンドをセミコロンで区切る。
意味
wordlistsの要素を左から順番に変数variableに代
入し、commandsを実行する。
セミコロンは改行で置き換えてよい。
for文はコマンドであり、コマンドが書けるところには自由に書くこ
とができる。例えばfor文の本体部分にfor文を書いてもよい。
for文の例2(打ち込んで確認)
#!/bin/sh
for D in `date`
do
echo $D
done
バッククオート`でコマンドを
囲むと、その部分がコマン
ドの実行結果で置き換えら
れる。
dateコマンの出力結果には
スペースが含まれており、ス
ペースで区切られた一つ一
つがDに代入され、echoで1
つずつ表示される。
for文の例3(打ち込んで確認)
#!/bin/sh
for L in *
do
echo $L
done
*はカレントディレクトリの
ファイルがスペースで区切
られたものに展開される。
$ echo *
で確認できる。
Lにはカレントディレクトリの
ファイル名が一つずつ代入さ
れ、それがechoで表示され
る。
for文の例4(打ち込んで確認)
#!/bin/sh
for F in *
do
cp $F $F.bak
done
Fにはカレントディレクトリの
ファイルのファイル名が一つ
ずつ代入され、それがcp
で.bakつきのファイルにコ
ピーされる。
for文の例5(打ち込んで確認)
#!/bin/sh
for F in *.c
do
echo $F
cp $F $F.bak
done
第一回目に書いた
シェルスクリプトは、
上記のシェルスクリプ
トの最後にexit 0を加
えたもの。
*.cは、カレントディレクトリ
において、ファイル名の最
後の部分が.cになっている
ファイルのファイル名がス
ペースで区切られたものに
展開される。
Fにはカレントディレクトリ
の.cで終わるファイル名が一
つずつ代入され、それが
echoで表示されたの
ち、.bakつきのファイルにコ
ピーされる。
構文
if文
if condition; then commands; [elif condition; then
commands;]… [else commands;] fi
condition: 条件
commands: コマンドをセミコロンで区切る。
[ ] はオプション(なくてもよいという意味)。
conditionの終了statusが0なら真、0以外は偽。
意味
conditionの終了statusが0ならthenパートを実行。
そうでなければelif以下、あるいはelseパートを(もし
あれば)実行。
セミコロンは改行で置き換えてよい。
if文はコマンドであり、コマンドが書けるところには自由に書くこと
ができる。例えばif文のelseパートにif文を書いてもよい。
終了status
コマンドは終了statusを返す。
シェルスクリプトではexit 0 等、exitの右に書く数によって
終了statusを指定する。
Cのプログラムでは、main関数のreturn文あるいはexitシ
ステムコールの引数によって終了statusを指定する。
コマンドの終了statusはシェルが受け取り、$?という変数
に保持している。
(例)
$ ls aaa
$ echo $?
もしaaaというファイルがなければlsコマンドの終了status
が1になっている。
if文の例1(打ち込んで確認)
#!/bin/sh
if test $1 -le $2
then
echo $1 is less than or equal to $2.
else
echo $1 is greater than $2.
fi
testは比較などさまざまな判定を行うコマンド。オ
プションによりさまざまな判定が行える。
test arg1 –le arg2 は、arg1がarg2より小さいか
どうか判定。
$1はシェルスクリプトの1番目の引数、
$2はシェルスクリプトの2番目の引数を表す。
if文の例2(打ち込んで確認)
#!/bin/sh
if diff $1 $2 > /dev/null
then
echo No differences were found between $1 and $2.
else
echo Some differences were found between $1 and $2.
fi
diffは2つのファイルの比較を行うコマンド。
終了statusは、同じとき0, 異なるとき1である。
diffコマンドの標準出力への出力は/dev/nullへリダイレクトさ
れるので捨てられる。(リダイレクトについて後日解説する)
testコマンドについて
testコマンドは大小比較などさまざまな判定に使われる。
数値比較、文字列比較、ファイル形式の判定、ファイルの
修正時刻の比較などがある。さらに、条件をand, or, notで
組み合わせることもできる。
testコマンドは非常によく使われるので略記法がある。
test …は [ … ] と略記してよい。
例えば、test $1 –le $2は、[ $1 –le $2 ]と書ける。
[の次の空白と、]の手前の空白は省けないので注意。
if文の例3(打ち込んで確認)
#!/bin/sh
if [ $1 -le $2 ]
then
echo $1 is less than or equal to $2.
else
echo $1 is greater than $2.
fi
例1におけるtest $1 –le $2を[ $1 –le $2 ]で置き
換えたもの。
ヒアドキュメント
シェルスクリプト内部で、コマンドへのキーボードからの入力を
シェルスクリプト内に直接書いておくことができる。
コマンド << str
………
str
のように、<<の右に区切り文字列を(自分で決めて)書き、その
次の行から、指定した文字列が最初に現れるまでの部分を、
ファイルからコマンドへ < でリダイレクトしたのと同等の効果が
ある。
ヒアドキュメントの例1(打ち込んで確認)
#!/bin/sh
cat << EOF
<html>
<body>
hello
</body>
</html>
EOF
上記のようにある程度長いメッセージを
出力したい場合、ヒアドキュメントを使うと
きれいに書ける。
ヒアドキュメントの例2(打ち込んで確認)
#!/bin/sh
cat << EOF > sample.c
#include <stdio.h>
int main (void) {
printf ("test\n");
return 0;
}
EOF
gcc -o sample sample.c
./sample
これはシェルスクリプト内でCのファイルを作成し、
コンパイルして実行する例である。
レポート課題2
課題2-1, 2-2, 2-3の3つの課題(後述)のシェ
ルスクリプトをkadai2-1.sh, kadai2-2.sh,
kadai2-3.shというファイル名で作成せよ。
レポートの提出方法
□ 下記のファイルを作成し、提出
• kadai2-1.sh, kadai2-2.sh, kadai2-3.sh, kadai2.txt
□ 提出方法
システムプログラミング講義用の課題提出用フォルダ内に
あるkadai2というフォルダの中に自分の学籍番号を名前と
するフォルダを作成し、その中に上記ファイルを置く。
kadai2.txt内に学籍番号、氏名、日付、および作成したプ
ログラムの簡単な説明を記載する。
□ 提出期限
11月17日の講義開始時間まで。締め切り後に提出した場
合、成績への反映を保証しない。
課題2-1
テキストファイルのファイル名をシェルスクリプトの引数とし
て受け取り、そのファイルが存在すればファイルの中身を
表示し、存在しない場合には、
ファイル名: No such file exists.
と表示して終了するようにせよ。実行方法は、
$ ./kadai2-1.sh test.txt
のようにファイル名を引数として与える。この場合、test.txt
というファイルが存在すれば中身を表示する(表示はcatコ
マンドを使えばよい)。存在しなければ上記のメッセージを
表示する。
ファイルの存在確認は、testコマンドを用いて、
test –f ファイル名
で行うようにせよ。コマンド “test –f ファイル名”
の終了statusは、そのファイルが存在して通常
のファイルなら真、そうでなければ偽である。
課題2-2
課題2-1のプログラムに,引数の個数チェックを
行う処理を追加し、ファイル名が引数に与えら
れなかった場合、
$ ./kadai2-2.sh
Usage:./kadai2-2.sh filename
のようにエラーメッセージを表示するようにせよ。
引数の個数は$#という変数に入っており、testコマンドで
test $# -eq 0 (あるいは [ $# -eq 0 ])
により、引数が0個かどうかを判定できる。
起動したコマンド名(この場合は./kadai2-2.sh)は$0に
入っているのでそれを使う。
課題2-3
テキストファイルのファイル名とキーワードをキーボードから受
け取り、指定した文字列を含む行を表示するシェルスクリプトを
grepコマンドを用いて作成せよ。
(実行例) text.txtのファイルの中にThis is a test. という行があ
る場合、
$ ./kadai2-3.sh
赤字の部分はキーボードか
Enter filename: test.txt
らの入力
Enter keyword: es
This is a test.
キーボードからの入力はreadコマンドで受け取る。
(例) read x
とすると、変数xにキーボードからの入力が入る。
メッセージ表示後に改行しないようにするには
echo –n ….
のようにすればよい。表示する文字列の最後に空白を入れた
い場合、表示する文字列を””で囲めばよい。
補足1
前回紹介したifコマンドはネストしてよい。例えば、elseパート有り
のifコマンドをネストして、
if …
then
…
else
if …
then
…
else
…
fi
fi
のような形で使える。これは外側のifコマンドにおけるelseパート
のコマンド(赤字の部分)がifコマンドになっているということである。
補足2
さきほどのifコマンドのネストは
if …
then
…
elif …
then
…
else
…
fi
のように書いてもよい。この場合はifコマンドのネストで
はなく、1つのifコマンドである。