システムプログラム(第10週): アクセス制御とスクリプト言語

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2007/No10.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2007/
http://www.coins.tsukuba.ac.jp/~yas/

捕捉

普通のローカル変数(auto変数)の番地を return してはならない。
f() {
    char buf[BUFFERSIZE];
    snprintf(buf,sizeof(buf),"hello");
    return( buf ); // 誤り
}
ローカル変数用のメモリは、関数から return したら、別の関数が利用する。
main() {
       f();
       g();
}

図? 関数main(),f(),g() が利用するメモリ。f() と g() が重なっている。

図? ローカル変数用のメモリ

関数から文字列を return する方法 ローカル変数用のメモリは、free() しないでも自動的(auto)に解放される。 ローカル変数の宣言に auto と付けてもよい。
f() {
    auto int x;// auto は省略可能。
    ...
}
前半の「バッファオーバーフロー」の説明参照。

アクセス制御とは

アクセス制御(access control) とは、「主体」が、「オブジェクト」を「操作」する時、どんな操作なら正し いということを定義して、それがきちんと守られていることをということを保 証することである。

図? 図? アクセス制御における主体、オブジェクト、および、操作

図? アクセス制御における主体、オブジェクト、および、操作

Unixにおけるアクセス制御とは

Unixでは、アクセス制御の主体は、プロセスである。 アクセス制御のオブジェクトは、ファイル、または、プロセスである。 操作には、次のようなものがある。

Unixは、マルチユーザのシステムなので、ユーザ1人ひとりを認識するような アクセス制御の仕組みを持っている。たとえば、Unix では、共同プロジェク トに関連したファイルは、他のユーザにも見せてもよいが、個人の電子メール は、他の人には見せないといった制御ができる。

これに対して、古いパソコン用のOS(Windows 95/98/ME, MacOS 9以下) では、このような複数のユーザを識別したアクセス制御は、できない。 Windows NT, Windows 2000, Windows XP, MacOS X は、可能である。

ファイルに対するアクセス制御

UNIXでは、ファイルの「内容」のアクセス制御を次の3段階で行う。
ユーザ
ファイルのUID(所有者)が、プロセスのUIDと同じ
グループ
ファイルのGIDが、プロセスのGIDのリストのどれかと同じ
その他
上の2つに当てはまらない時
これをつかって、モード属性の下位9ビットのうち、どの3ビットを使うかを 決める。そして、そのビットが1になっていれば、その操作が許される。

ファイルの「内容」のアクセス3段階であるが、ファイルの「属性」次の2段 階である。

ユーザ
ファイルのUID(所有者)が、プロセスのUIDと同じ
それ以外
ファイルのUID(所有者)が、プロセスのUIDと異なる
ユーザの権限では、ファイルの属性(モード、グループ、時刻)を変更する ことができる。それ以外の権 限では、属性を読み出すことはできるが、変更は一切できない。 つまり、ファイルの内容がアクセスできなくても、ls -l, stat(2) で 属性を調べることはでる。

プロセスに対するアクセス制御

プロセスのアクセス制御は、次の2段階で行なう。
同一ユーザ
操作対象のプロセスが、操作するプロセスのUIDと一致している。
それ以外
操作対象のプロセスが、操作するプロセスのUIDと一致していない。
プロセスの操作としては、シグナルを送ることができるかどうか (kill() システムコール) と、デバッガで デバッグすることができるとか(ptrace() システムコール)、トレースを調べることができるかなどが ある。それらの操作は、同一ユーザの場合 許され、そうではない場合は、許されない。

スーパーユーザ(root)

umaskの影響

WWWページに対するアクセス制御

WWWは、もともと大勢の人にメッセージを伝えるための仕組である。 あるページは、ある特定の人(同じ研究室・部署の人)だけに しか見えないように設定することもできる。

WWW ページのアクセス制御、

主体
WWW ブラウザ(を使っている人、ホストのIPアドレス)
オブジェクト
サーバ側にあり、URL で示されるもの(ファイル、ディレクトリ、CGI のプログラム)、
操作
GET、POST など

アクセス制御の方針(ポリシー)は、サーバ側で既述される。 この時、次のような情報がよく使われる。

アクセス制御は、基本的には、WWWサーバの管理者の仕事である。 WWWサーバ (httpd) の設定ファイル ( /usr/local/etc/httpd/conf/httpd.conf, /var/www/conf/httpd.conf など ) に記述される。

個人のページについては、 .htaccess というファイルを作れば、個人で変更できるようになっている場合がある。

IPアドレスによるアクセス制御

IPアドレスにより筑波大学の学内にだけアクセス可能なページを作るための .htaccess の例。
order deny,allow
deny from all
allow from 130.158.0.0/16 133.51.0.0/16 192.50.17.0/24

パスワードが設定されたページのアクセス

例:パスワード・ファイル /home/lab/Denjo/yas/etc/passwd-doc1 に登録されているユーザだけが、 .htaccess があるディレクトリ以下にあるファイルをアクセスできる。

AuthType Basic
AuthName "Members Only"
AuthUserFile /home/lab/Denjo/yas/etc/passwd-doc1
require valid-user
このパスワード・ファイルは、htpasswd というプログ ラムで作る。
% ls /home/lab/Denjo/yas/etc/passwd-doc1 [←]
ls: /home/lab/Denjo/yas/etc/passwd-doc1: No such file or directory
% htpasswd -c /home/lab/Denjo/yas/etc/passwd-doc1 user1 [←]
Adding password for user1.
New password:user1のパスワードを打ち込む[←]
Re-type new password:user1のパスワードを打ち込む[←]
% htpasswd /home/lab/Denjo/yas/etc/passwd-doc1 user2 [←]
Adding user user2
New password:user2のパスワードを打ち込む[←]
Re-type new password:user2のパスワードを打ち込む[←]
% cat /home/lab/Denjo/yas/etc/passwd-doc1  [←]
user1:1fjr1tHIgoG7U
user2:qXaeA9Zge7Yqc
% []
一番最初は、-c オプション付で実行する。passwd コマ ンドと同様に、打ち込んだパスワードは、画面には表示されず、また、確 認のために2回打つ必要がある。

htpasswd コマンドの結果、次のようなファイルが作られる。

user1:1fjr1tHIgoG7U
user2:qXaeA9Zge7Yqc
ユーザ名と暗号化されたパスワード(ハッシュ値)から構成される。 このファイルは、~/public_html 以外の場所に置くと GET で盗まれることはない。

このアクセス制御が有効なファイルをクライアントが GET したとする。

GET /dir1/file1.html HTTP/1.0[←]
[←]
すると、サーバから次のようなエラーが返される。
HTTP/1.1 401 Authorization Required
WWW-Authenticate: Basic realm="Members Only"
Content-Type: text/html

<HTML><HEAD>
<TITLE>401 Authorization Required</TITLE>
</HEAD><BODY>
...
</BODY></HTML>
.htaccessAuthTypeAuthName の内容が、HTTP の応答の WWW-Authenticate: に現れる。

このページをアクセスするには、クライアント(WWWブラウザ)は、ウインドウ を開いてユーザにユーザ名とパスワードを要求する。そして、次のような GET 要求をもう一度送る。

GET /dir1/file1.html HTTP/1.0[←]
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==[←]
[←]
Authorization: には、ユーザが打ち込んだユーザ名とパスワードが Base64 (64進数)で符合化されて含まれている。 これを受け取ったサーバは、 AuthUserFile で指定されたファイルを開いて、ユーザ名とパスワー ドパスワードを照合して、正しければアクセスを許する。

Base64 は、「暗号ではない」ので、簡単に元にもどせる。

% echo QWxhZGRpbjpvcGVuIHNlc2FtZQ== | openssl base64 -d [←]
Aladdin:open sesame% []
AuthType Basic を使うアクセス制御の方法では、パスワードがそのままネッ トワークを流れてしまうので、盗聴に弱い。

LDAPによるアクセス制御

情報学類では、Web ページに対するアクセス制御として、Unix に対するログイ ンのユーザ名とパスワードを使う方法を利用可能にしている。内部的には、 LDAP を用いている。

情報学類コンピューティング環境/web環境 https://www.coins.tsukuba.ac.jp/ce/index.php?web%B4%C4%B6%AD

この機能を使う時には、SSL 付きのサーバ (http: ではなく https:) を使うこと。

スクリプト言語

コンピュータ言語の種類 スクリプト言語とは、アプリケーション本体ではなく、アプリケーションの細 かな動作を変更したり、アプリケーション本体を変更することなく機能を追加 したりするために使われる言語。

インタプリタとスクリプト

スクリプト言語は、多くの場合、インタプリタで実行される。

プロセスはインタプリタの実行形式(機械語)から作られ、インタプリタのソー ス・プログラムは、そのプロセスが読み込む単なるデータとなる。

Unix には、スクリプトを簡単に実行する仕組みとして #!がある。

インタプリタ/bin/cat

標準入力を標準出力に出力する cat コマンド 「#!」の働きを調べる。 cat は「プログラムを表示する」インタプリタである。 まず、「catインタプリタ」用のプログラムを作成する。
% cat > run-cat [←]
#!/bin/cat[←]
hello[←]
^D
% chmod +x run-cat [←]
% ls -l run-cat [←]
-rwxr-xr-x  1 yas            20 Sep 13 04:11 run-cat
% []
「catインタプリタ」用のプログラムを実行する。
% ./run-cat [←]
#!/bin/cat
hello
% []
これは次のように実行したものと同じになる。
% /bin/cat ./run-cat [←]
#!/bin/cat
hello
% []

一般のインタプリタ

「#!」の形式は次のようになる。

インタプリタの実行形式の絶対パス名: /dir/interpreter

そのソース・プログラムのファイル名: run

run の内容:

#!/dir/interpreter arg-1 arg-2 arg-3
<以下、プログラム>
このファイルに実行可能属性を付ける(chmod +x)と、 ファイル名を入力して実行することができる。
% chmod +x ./run [←]
% ./run arg-a arg-b arg-c [←]
<実行結果>
これは、次のようにインタプリタを起動したものと同じ結果になる。
% /dir/interpreter ./run 'arg-1 arg-2 arg-3' arg-a arg-b argc [←]
./run ファイルに書いた引数は、シェルから実行する時場合には、 ''で囲まれた時と同じような形式でインタプリタのプロ セスへの引数として渡される。

#!の解釈

#!」行は、Unix のカーネルが解釈する。 シェルが、ファイルの先頭を読み、指定されたインタプリタを起動するのでは ない。「#!」行では、シェル変数、環境変数、エイリアスはつ かえない。

スクリプト言語のプログラムでは 「#」から始まる行がコメントであると都合がよい。

awksed のように、プログラムが含まれたファイルを指定 する時に -f (program file) オプションが必要なものは、次の ように、この行に -f を付ける。

#!/bin/awk -f
{ print }

シェル

シェルはインタプリタであり、シェル・スクリプトはシェル・インタプリタの プログラムである。

シェル・スクリプトの先頭の #!/bin/sh#!/bin/csh は、 そのインタプリタを起動するという意味である。

csh

Coins での標準のログインシェルは、tcsh。

tcsh は、csh に terminal での編集機能や補完機能を付けたもの。シェル・ スクリプトを書く時には、多くのシステムで備わっている /bin/csh を使うこ とが多い。

cshスクリプトの作り方

まず端末から実行してみる。 端末から打ち込んだものを結果を、ファイルに保存する。

% gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o prog [←]
(^p で1行もどす。^a で、行頭に移動して echo と打ち、^e して > run と打つ)
% echo gcc -I/usr/local/include/ file1.c -llib1 -llib2 -o prog > run [←]
% csh run [←]
% []
tcsh の機能で、^p (Control+P) で1行戻して、echo でファイルに落とす。 「|」があれば、'' でくくる。csh の引数にファイル名を与えて実行できる。

いちいち csh と打たないでもいいようにするには、chmod する。

% chmod +x run [←]
% ./run [←]
% []
#! がなければ、execve() システム・コールがエラーになる。その場合、tcsh が sh か csh (先頭が#の時)を実行する。

必要なら、エディタで「#!/bin/csh」か「#!/bin/sh」を入れる。

数行にわたるものの場合、history コマンドを使う。

% history [←]
% history | tail -5 > run [←]
% emacs run [←]

「#!/bin/csh -f」と、-f を付けた方が、~/.cshrc を読み込まないので起動 が速い。ただし、~/.cshrc での設定(aliasなど)は効かないことがある。環境 変数は、今の状態が引き継がれる(~/.cshrc を読み込ませない方が都合がよい ことが多い)。

デバッグ

-x オプションを付けて実行すると、画面にスクリプトを表示さながら実行す る。
% csh -f -x run [←]

-n オプションを付けて実行して、構文のチェックだけ行う。

シェルに1行ずつ与えて実行してみる。

シェル変数path(環境変数PATH)とrehash

~/.cshrcなどを設定して、~/bin をpath シェル変数(PATH 環境変数)に含まれるようにすることを奨める。そして自分で作成したプログ ラムやスクリプトを、~/bin に置くと./ などで実行する必 要はない。 ただし、ファイルを作成し、chmod +x した後で、1度だけ rehash コマンド を打つ必要がある。
% emacs ~/bin/newcommand [←]
% chmod +x ~/bin/newcommand [←]
% rehash [←]
% newcommand [←]
% emacs ~/bin/newcommand [←]
% newcommand [←]
% emacs ~/bin/newcommand [←]
% newcommand [←]
% []
chmod も rehash も、シェルごとに1度だけやればよい。端末をたくさん開い ていた時には、作成したスクリプトをすぐに使いたい時にはそれぞれのシェル でrehash コマンドを実行する。

rehash の意味は、ハッシュ表を作り直すことである。path にあるコマンドは、 csh は、コマンドを打つたびに探すのではなくてハッシュ表に入れてそれを検 索している。

rehash は、新しいシェルが実行される時には自動的に行われている。次にロ グインした時、chmod +x した後に開いた端末ではrehash を実行する必要はな い。

cshスクリプトでよく使われる機能

shスクリプトでよく使われる機能

シェルスクリプト作成の時のlsとgrep

ls の動き

ls コマンドは、標準出力が端末ではない(パイプやファイル)の場合、結果 を縦に並べて出力する。
% ls [←]
file1  file2  file3
% ls | cat [←]
file1
file2
file3
% []
強制的に縦に並べるオプション ls -1 や横に並べるオプション ls -C もある。

grep

grep (egrep, fgrep) は、ファイルの内容を検索し、引数で与えられたパタン が見つかった行を出力するコマンドである。次の例は、「UTMP_FILE」 または、「WTMP_FILE」という文字列を含む行を、ファイル /usr/include/utmp.h から探し、結果を表示している。
% egrep '_PATH_[UW]TMP' /usr/include/utmp.h [←]
#define _PATH_UTMP      "/var/run/utmp"
#define _PATH_WTMP      "/var/log/wtmp"
% []
一般的には、次のようになる。
% egrep pattern file1 file2 ... [←]
pattern としては、次のような正規表現(regular expressions)が使える。
.
任意の1文字
.*
任意の1文字が0回以上繰り返したもの
A*
文字Aが0回以上繰り返したもの
A*
文字Aが0回以上繰り返したもの
^
行の先頭
$
行の末尾
[a-m]
文字aから文字mまでの1文字
[A-Za-z0-9]
アルファベットと数字
\c
c そのもの。\. や \*, \$ などで使う。
patter1|patter2
patter1、または、patter2 (egrepのみ)
シェルで「*」と書く所は、grep では「'.*'」と書く点に注意する。 たとえば、 シェルで「*.[ch]」と書く所は、grep では「'.*\.[ch]'」と書く。

-v オプションを付けると、マッチしなかった行が表示される。

-s オプションを付けると、パタンを含むか含まないかの判定だけを行い、画 面に結果を表示しない。シェル・スクリプトの if 文の中で使う。

「*」、「$」、「[]」など、シェルが展開してしまう文字をパタンとして指定 する時には、シングル・クォート「' '」で括る。

egrep は、機能が拡張され、アルゴリズムも改良されていて速い。

RubyによるCGI

cgi-printarg.c を Ruby で書直す。
   1:	#!/usr/bin/ruby
   2:	# cgi-printarg-ruby.cgi -- CGI プログラムに対する引数を表示するプログラム
   3:	# ~yas/syspro/www/cgi-printarg-ruby.cgi
   4:	# Created on 2005/06/27 01:52:36
   5:	
   6:	require "cgi"
   7:	
   8:	def main()
   9:	        $SAFE = 1
  10:	        @cgi = CGI.new()
  11:	        print_header()
  12:	        print_content()
  13:	        exit( 0 )
  14:	end
  15:	
  16:	def print_header()
  17:	        printf("Content-Type: text/html\n")
  18:	        printf("\n")
  19:	end
  20:	
  21:	def print_content()
  22:	        printf("<HTML><HEAD></HEAD><BODY><PRE>\n")
  23:	        printf("request_method: %s\n",e(@cgi.request_method))
  24:	        printf("script_name: %s\n",e(@cgi.script_name))
  25:	        printf("query_string: %s\n",e(@cgi.query_string))
  26:	        printf("content_length: %d\n",@cgi.content_length)
  27:	        qh = @cgi.keys
  28:	        i = 0
  29:	        qh.each { |name|
  30:	            val = @cgi[name]
  31:	            printf("qv[%d]: %s=%s \n",i,e(name),e(val) )
  32:	            i = i + 1
  33:	        }
  34:	        printf("</PRE></BODY></HTML>\n")
  35:	end
  36:	
  37:	def e( str )
  38:	        return( str == nil ? "" : CGI::escapeHTML(str) )
  39:	end
  40:	
  41:	main()

Ruby では、require で、必要なライブラリを読み込む。 def から end までがメソッドの定義である。

main() では、CGI.new() により、CGI クラスのインスタンスを生成している。 その結果を @cgi という変数(インスタンス変数、mainの外でも使える)に保存 している。

このプログラムでは、main() という名前のメソッドを定義しているが、、 main() というメソッドから実行が開始させるわけではない。メソッド定義で はないものは、即座に実行される。このプログラムは最後に main() を呼び出 す文がある。これを忘れると何も実行されない。

$SAFE は、グローバル変数である。Ruby では、ファイルからの入力や環境変 数は汚染されたものとして扱われる。$SAFE を 1 にすると、汚染された文字 列でファイルを開くとエラーになる。標準では、0 。安全を確認したら、 obj.untaint() メソッドで汚染を解除する。明示的に obj.taint() で汚染さ せることもできる。

print_header()では、HTTP のヘッダのうち、Content-Type: 行だけを 出力している。

print_content() では、本文を出力している。

関数(メソッド) e() では、CGI ライブラリ (CGIクラス)の CGI::escapeHTML() を呼び出して、安全なものにして表示する。 たとえば、「<」は、「&lt;」と変換している。こ れで、<SCRIPT> のような危険なスクリプトが送り込まれたとし ても「&lt;SCRIPT&gt;」と表示と表示されるだけで、スクリプト は実行されない。

@cgi に保存されたCGI クラスのインスタンスの request_method() メソッド を呼び出すと、"GET" か "POST" が返される。環境変数は、ハッシュ表 ENV に対して ENV['REQUEST_METHOD'] のようにしてもアクセスできるが、環境変 数を CGI クラスで変更してしまうこともあるようである。

@cgi.keys により、パラメタの一覧が配列の形で得られる。配列の各要素につ いて(qh.each)、パラメタ名を得て表示している。この例では、どんなパラメ タでも表示しているので、このようなループになっているが、通常の CGI プ ログラムでは、@cgi['パラメタ名'] のようにして、パラメタの値を文字列と して取り出すだけでよい。

表示例:

CGI の GET メソッドを使う例

姓: 名:


その他
電子メール:

CGI の POST メソッドを使う例

姓: 名:


その他
電子メール:

実行例:

request_method: GET
script_name: /~yas/coins/syspro-2007/No10_files/cgi-printarg-ruby.cgi
query_string: lastname=arg1&firstname=arg2&sex=Male&email=arg3
CONTENT_LENGTH: 
qv[0]: firstname=arg2 
qv[1]: lastname=arg1 
qv[2]: sex=Male 
qv[3]: email=arg3 
request_method: POST
script_name: /~yas/coins/syspro-2007/No10_files/cgi-printarg-ruby.cgi
query_string: 
CONTENT_LENGTH: 48
qv[0]: firstname=arg2 
qv[1]: lastname=arg1 
qv[2]: sex=Male 
qv[3]: email=arg3 

コマンドラインからの実行

CGI クラスを使ったプログラムは、 環境変数を設定したデバッグ の他に標準入力から パラメタを与えてでデバッグすることもできる。
% echo "a=b&c=d" | ./cgi-printarg-ruby.cgi [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY><PRE>
request_method: 
script_name: 
query_string: 
CONTENT_LENGTH: 
qv[0]: a=b 
qv[1]: c=d 
</PRE></BODY></HTML>
% []

練習問題と課題

練習問題(1001) idコマンド

id コマンドと似たような動きをするコマンドを作りなさい。id コマンドは、 次のように、プロセスの UID, GID を表示するコマンドである。
% id [←]
uid=1013(yas) gid=40(lab) groups=40(lab),510(softadm),500(jikken3)
% []
ここで、uid は、getuid()、 gid は、getgid()、groups は、getgroups() シ ステムコールの結果である。getgroups() では、複数の GID が返される。

練習問題(1003) chmod プログラム

ファイルのモードを変更するコマンド chmod に似たコマンドを作りなさい。
% mychmod 755 filename [←]
モードは、8進数で与えるものとする。引数として取ることができるファイル 名は、1つだけでよい。

練習問題(1004) touch プログラム

ファイルの時刻を変更するコマンド touch に似たコマンドを作りなさい。
% mytouch filename [←]
この結果、ファイルの最終更新時刻が現在の時刻になる。

ヒント:time(2) と utime(2) を使う。

練習問題(1005) 条件コピー

ファイルが更新されていた時だけコピーするようなプログラムを作りなさい。

ヒント:stat(2)システムコールで、コピー元ファイルとコピー先ファイル最 終更新時刻を調べる。もし、前者が新しければ、コピーする。後者が新しけれ ば、なにもしない。

練習問題(1006) 属性まで含んだコピー

cp -p (Preserve) と同じように、ファイルの内容に加えていくつかの属性を 保存しながらファイルをコピーするプログラムをつくりなさい。

この課題では、次のような属性を保存しなさい。

UID (owner) や GID については、保存しなくてもよい。UID の保存は、 スーパーユーザしかできない。 スーパーユーザが実行した時には、保存できるようなプログラムを作成しても よい。一般ユーザで実行した時には、エラーを無視してよい。

コピーする時に、内容をコピーした後に、コピー元のファイルに stat() を実 行する。こうして得られた属性を、chmod() や utime() でコピー先のファイ ルに設定する。

練習問題(1007) IPアドレスによるアクセス制御

特定のホスト(たとえば、azalea10)からだけしかアクセスできないような .htaccess を定義しなさい。

練習問題(1008) パスワードによるアクセス制御

ある特定のユーザ名とパスワードを知っている人だけからしか アクセスできないような .htaccess を定義しなさい。 そして、htpasswd コマンドを使って、パスワードを設定しなさい。

注意:ユーザ名は、長くてもよい。Unix や Windows のユーザ名とは別のもの でよい。

注意:ここで設定したパスワードは、ネットワーク上を暗号化されずに流れる。 非SSLのWebサーバで 実験する時には、決して Unix や Windows のログインのパスワードと 同じものを使ってはならない。

練習問題(1009) C言語のソース・プログラムだけを表示するls

与えられたディレクトリのC言語のソース・プログラムだけを表示するシェル・ スクリプトを作りなさい。
% ls-c ~/syspro/file/ [←]
fd-print.c
file-copy.c
mmap-head.c
stdio-thru.c
utmp-print.c
wtmp-last10.c
ystat.c
ystat.h
ヒント:ls の結果から、egrep で .c や .h で終わるものを抜き出す。ある いは、シェルのパタン・マッチの機能を使って、*.c や *.h だけを取り出す。

余裕があれば、-l などのオプションが付けられるようにしなさい。

練習問題(1010) 「.」から始まるファイルだけを表示するls

ls コマンドや csh, tcsh, sh の * では、「.」から始まるファイルは表示 されない。逆に「.」から始まるファイルだけを表示する ls コマンドを 作りなさい。
% ls-dot ~ [←]
.
..
.cshrc
.emacs
.login
% []
ヒント:ls に -a オプションを付けると、全てのファイルを表示する。 先頭が「.」のものを抜き出す。

csh, tcsh, sh で 「.*」と指定して探す方法もある。

余裕があれば、-l などのオプションが付けられるようにしなさい。

練習問題(1011) ディレクトリだけを表示するls

ディレクトリだけを表示する ls コマンドを作りなさい。
% ls-dir ~ [←]
Mail
lib
syspro
tmp
% []
ヒント:ls -l で、d から始まる行だけを抜き出す。あるいは、csh の -d や test の -d でディレクトリだけを選びだす。

できるだけ上のようにディレクトリの名前だけ表示するようにしなさい。 モードや日付は表示しないようにしなさい。

余裕があれば、-l などのオプションが付けられるようにしなさい。

練習問題(1012) ファイルの大きさの順に表示するls

ls -l は、ファイルの名前の順に表示する。これを、ファイルの大きさの順に 表示するシェル・スクリプトを作りなさい。
% ls-size ~yas/syspro/www [←]
-rwxr-xr-x   1 yas  prof  27056 Jun 12 11:54 cgi-printarg.cgi
-rwxr-xr-x   1 yas  prof  18376 Jun 19 01:29 cgi-hello.cgi
-rw-r--r--   1 yas  prof   5287 Jun 12 11:54 cgi-printarg.c
-rw-r--r--   1 yas  prof   1579 Sep 26  2002 get.c
-rwxr-xr-x   1 yas  prof    985 May 20 17:17 cgi-printarg-ruby.cgi
-rw-r--r--   1 yas  prof    486 Jun 11 21:20 cgi-hello.c
-rw-r--r--   1 yas  prof    297 Sep 22  2003 Makefile
drwxr-xr-x   5 yas  prof    170 Jun 19 01:27 CVS
-rw-r--r--   1 yas  prof     54 Feb 14  2004 data
total 128
% []
ヒント:ls -l の出力を sort コマンドでソートする。 ソートは、第5フィールドで行う。

練習問題(1013) ファイルの行数の順に表示するwc

wc コマンドは、ファイルの行数、単語数、バイト数を表示する。wc コマンド は、引数で与えられた順に表示する。これを行数の順に表示するようなスクリ プトを作りなさい。
% wc-lines *.c [←]
      85     228    1836 proc-uid-print.c
      75     187    1156 pipe-rw-dup.c
      50     152    1141 vaddr-print.c
      46     140    1071 proc-create.c
      67     161    1014 pipe-rw-nodup.c
      38      93     819 signal-int.c
      50      98     802 setjmp-longjmp.c
      32      90     561 run-n.c
      27      66     535 home-print.c
      20      50     424 cont-1.c
      20      50     424 cont-0.c
      25      60     419 t-system.c
      20      40     384 exec-date.c
      14      49     370 arg-print.c
      15      44     355 env-print.c
      12      32     305 cont-2.c
      13      16     174 fork-hello.c
       4      10      60 main-return.c
% []
この課題では、合計(Total)は表示されなくてもよい。

ヒント:wc の出力を sort コマンドでソートする。foreach か for で1つず つ wc コマンドを実行して、全体の結果を sort するか、引数 $* で wc した 後、sort して、合計(Total)の行を削る。

練習問題(1014) 使っているメモリのサイズが大きいプロセスの表示

ps aux では、全プロセス(a)が、サイズなどの情報も含めて(u)表示される。
% ps aux  [←]
USER       PID %CPU %MEM      VSZ    RSS  TT  STAT STARTED      TIME COMMAND
root         1   0.0 -0.0    28340    540  ??  S<s   6Jun06   0:22.58 /sbin/laun
root        52   0.0 -0.0    27256    156  ??  Ss    6Jun06   0:00.00 /sbin/dyna
root        58   0.0 -0.2    30632   3536  ??  Ss    6Jun06   0:08.21 kextd
root        91   0.0 -0.0    28240    624  ??  Ss    6Jun06   0:00.03 /usr/sbin/
...
root        52   0.0 -0.0    27256    156  ??  Ss    6Jun06   0:00.00 /sbin/dyna
root        58   0.0 -0.2    30632   3536  ??  Ss    6Jun06   0:08.21 kextd
root        91   0.0 -0.0    28240    624  ??  Ss    6Jun06   0:00.03 /usr/sbin/
root        92   0.0 -0.2    28360   3624  ??  Ss    6Jun06   9:41.29 /usr/sbin/
% []
そのうち、メモリのサイズ(RSS)が大きいプロセスを 10 個だけ表示するシェル・ スクリプトを作りなさい。
% ps-rss-top10 [←]
USER       PID %CPU %MEM      VSZ    RSS  TT  STAT STARTED      TIME COMMAND
root       239   0.0 -0.9    62428  18316  ??  Ss    6Jun06   2:16.88 /System/Li
security 21102   0.1 -0.8   247056  15876  ??  S    Sat04PM   5:17.79 /System/Li
windowse 21096   0.1 -0.5   203808  11108  ??  Ss   Sat04PM   4:32.14 /System/Li
daemon     242   0.0 -0.4    73932   9280  ??  Ss    6Jun06   1:41.18 /System/Li
root       115   0.0 -0.4    39260   7920  ??  Ss    6Jun06   1:50.21 /System/Li
root       104   0.0 -0.2    34128   5100  ??  Ss    6Jun06   2:47.36 /usr/sbin/
root        92   0.0 -0.2    28360   3624  ??  Ss    6Jun06   9:41.81 /usr/sbin/
root        58   0.0 -0.2    30632   3536  ??  Ss    6Jun06   0:08.21 kextd
root       132   0.0 -0.2    31460   3372  ??  Ss    6Jun06   1:21.59 /usr/sbin/
root     21094   0.0 -0.1   205696   2924  ??  Ss   Sat04PM   0:00.19 /System/Li
ヒント:1行目は、そのまま表示する。 RSS の順(第6フィールド)に sort して head する。

余裕があれば、VSZ の順、CPU 時間の順に表示するスクリプトを作りなさい。

類似のことを実行するプログラムとして top がある。

練習問題(1015) バックアップ・ファイルとのdiff

emacs は、ファイルを保存する時に、1つの前のバージョンを「~」を 付けて保存する。そのようなファイルを見つけて、オリジナルのファイルと diff コマンドで比較するようなスクリプトを作りなさい。
% diff-backup *.c [←]
ヒント:foreach または for 文で、引数のファイルについて、 "$file"~ のような名前のファイルが存在するかを調べる。 存在すれば、diff コマンドで表示する。

余裕があれば、比較しているファイルの名前を表示したり、ファイルごとに停 止する、引数を取る、diff に対するオプションを取る、などの工夫をしなさ い。

練習問題(1016) 小文字のファイル名への変更

Windows 系のコンピュータから Unix へファイルをコピーすると大文字のファ イル名になってしまうことがある。そのようなファイル名を全て小文字にする ようなスクリプトを書きなさい。

% mv-lower [A-Z]* [←]

ヒント:ファイル名を echo して、tr で小文字にして、それを `` でシェル変数に入れる。元の名前から小文 字の名前に mv で変える。

余裕があれば、大文字と小文字を変換することで、ファイルが上書きされる時 には警告を出したり、ユーザに問い合わせたりするようにしなさい。

練習問題(1017) カウント・ダウン

秒単位でカウント・ダウンをするようなシェル・スクリプトを作りなさい。
% countdown 5 [←]
5
4
3
2
1
0
% []
ヒント:sleep 1 で、1ごとに止める。

練習問題(1018) ファイルのn行目からm行目までの表示

引数として2つの数 n, m 、および、ファイル名を取り、そのファイルの n 行めから m 行目までを表示するシェル・スクリプトを作りなさい。 たとえば、次の例では、ファイルの 10 行目から 20 行目までを表示する。
% show-n-m 10 20 filename [←]

余裕があれば、-n オプションを付けなさい。これは、ファイルに行番号を振 るものである。nl コマンドを使うとよい。(cat -n が使えるシステムもある。)

練習問題(1019) シェル・スクリプト自由課題

その他、上であげた課題と同程度に難しい課題を自由に設定して解きなさい。

練習問題(1020) CGIによる足算(Ruby)

練習問題(904) CGIによる足算を Ruby で書直しなさい。

ヒント:strtol() のように、文字列から整数値を得るには、Ruby では、 Integer() を使って次のように行う方法がある。

     s = "100"
     i = Integer(s)

練習問題(1021) カレンダの表示(Ruby)

練習問題(905) カレンダの表示を Ruby で書直しなさい。
Last updated: 2007/06/19 21:14:37
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>