スクリプト言語とウインドウ

システム・プログラム

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

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

■今日の重要な話

■スクリプト言語

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

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

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

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

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

◆インタプリタ/bin/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
% []
----------------------------------------------------------------------
実行する。
----------------------------------------------------------------------
% ./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

O2での標準のログインシェルは、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行ずつ与えて実行してみる。

◆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 '[UW]TMP_FILE' /usr/include/utmp.h [←]
#define UTMP_FILE       "/var/adm/utmp"
#define WTMP_FILE       "/var/adm/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 は、機能が拡張され、アルゴリズムも改良されていて速い。

■シェル変数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 を実行する必要はな い。

■GTK+によるウインドウ・プログラミング

◆GTK+とWidgetセット

GTK+ とは、グラフィカル・ユーザ・インタフェースのアプリケーションを簡 単に開発するためのライブラリである。GTK+ は、GIMP Tool Kit の略であり、 もともとGIMP (GNU Image Manipulation Program) という画像編集ソフトウェ ア(coins で gimp と打つと利用可能)を作成するために作られたものである。 GTK+ は、その後 GNOME を作るためにも使われている。

GTK+ は、もともとは X ウインドウ上の widget セットとして作られた。 widget とは、ボタンやテキストボックスやスクロールバーななど部品を意味 する。X ウインドウでよく使われる widgetセットやライブラリには、次のよ うなものがある。

GTK+ は、C言語で書かれているが、C言語以外の言語からも使えるようにな つている。Ruby、Perl、Python のようなスクリプト言語からも使える。

GTK+ は、Windows 用, MacOSX用 も存在する。

◆初期化とイベントループ(gtk-hello.c)


----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        gtk-hello.c -- GTK+を使ってウインドウに文字列を表示するプログラム
   4:	        ~yas/syspro/window/gtk-hello.c
   5:	        Start: 2003/06/21 22:57:52
   6:	*/
   7:	
   8:	#include <gtk/gtk.h>
   9:	
  10:	void my_delete_event()
  11:	{
  12:	        printf("my_delete_event\n");
  13:	        gtk_main_quit();
  14:	}
  15:	
  16:	main( int argc, char *argv[] )
  17:	{
  18:	    GtkWidget *window;
  19:	    GtkWidget *label1;
  20:	
  21:	        gtk_set_locale();
  22:	        gtk_init( &argc, &argv );
  23:	        window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
  24:	        gtk_signal_connect( GTK_OBJECT(window), "delete_event",
  25:	                            GTK_SIGNAL_FUNC(my_delete_event), NULL );
  26:	        gtk_signal_connect( GTK_OBJECT(window), "destroy",
  27:	                            GTK_SIGNAL_FUNC(my_delete_event), NULL );
  28:	        gtk_container_set_border_width( GTK_CONTAINER(window), 50);
  29:	
  30:	        label1 = gtk_label_new("Hello,world!");
  31:	        gtk_container_add( GTK_CONTAINER(window),label1 );
  32:	
  33:	        gtk_widget_show( label1 );
  34:	        gtk_widget_show( window );
  35:	        gtk_main();
  36:	        printf("return from gtk_main()\n");
  37:	        exit( 0 );
  38:	}
----------------------------------------------------------------------

GTK+ のプログラムでは、ヘッダファイル gtk/gtk.h を読み込む。

my_delete_event() は、(トップレベルのウインドウが) "delete_event"、ま たは、"destroy" というイベントを受け取った時に実行される関数である。 イベントとは、何かが生じた(ボタンが押された、キーボードが打たれた、マ ウスがウインドウに入った)ことの知らせである。"delete_event" や "destroy" は、プログラムが終了するようにというイベントである。 GTK+ では、イベントのことを「シグナル」と呼んでいる。

gtk_set_locale() や gtk_init() は、GTK+のプログラムを作成する時の定石 (決り文句)である。gtk_rc_parse() でアプリケーションごとの設定ファイ ルを読み込むこともある。

main() では、ウインドウ(トップレベル)を1つ作っている。内部にボタンを 作ると、ボタンも1つのウインドウになる。一番外側のウインドウがトップレ ベルのウインドウである。

ウインドウに何か文字を表示するには、まず、ラベルと呼ばれる GtkWidget を作る。それを表示したいウインドウに gtk_container_add() で張り付ける。 ウインドウの種類の中でも、ラベルを張り付けられるのは、コンテナ (container) の一種(サブクラス)だけである。

作成した widget (ウインドウ)は、show することで表示されるようになる。 show を忘れると表示されない。

gtk_main() は、GTK+のイベント・ループである。 イベント・ループは、次のような操作を行う。

このプログラムは、終了ボタンもない。ウインドウ・マネジャの機能でdelete か destroy というイベントを生じさせると、my_delete_event() が呼ばれる。 この中で、gtk_main_quit() を呼ぶと、gtk_main() から終了する。

◆GTK+のプログラムのコンパイルと実行

コンパイルするには、-I でヘッダファイル gtk/gtk.h 等の位置を指 定する。さらに、-l で必要なライブラリ(libgtk-1.2 や libX11)をリンクす る。-I や -l で何を指定すればよいかは、gtk-config というコマンドを実行 するとわかる。
----------------------------------------------------------------------
% cp ~yas/syspro/window/gtk-hello.c . [←]
% cp ~yas/syspro/window/Makefile . [←]
% make -n gtk-hello [←]
cc `gtk-config --cflags` -o gtk-hello gtk-hello.c `gtk-config --libs`
% gtk-config --cflags [←]
-I/usr/include/gtk-1.2 -I/usr/include/glib-1.2 -I/usr/lib/glib/include -I/usr/X11R6/include
% gtk-config --libs [←]
-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXi -lXext -lX11 -lm
% make gtk-hello [←]
cc `gtk-config --cflags` -o gtk-hello gtk-hello.c `gtk-config --libs`
% ./gtk-hello  [←]
^C
% []
----------------------------------------------------------------------

図? gtk-hello の実行結果のウインドウ

図? gtk-hello の実行結果のウインドウ

◆ボタンが押された時の処理(gtk-button3.c)


----------------------------------------------------------------------
   1:	
   2:	/*
   3:	        gtk-button3.c -- GTK+を使ってウインドウにボタンを表示するプログラム
   4:	        ~yas/syspro/window/gtk-button3.c
   5:	        Start: 2003/06/21 22:57:52
   6:	*/
   7:	
   8:	#include <gtk/gtk.h>
   9:	
  10:	void my_delete_event()
  11:	{
  12:	        printf("my_delete_event\n");
  13:	        gtk_main_quit();
  14:	}
  15:	
  16:	void button3_clicked(GtkWidget *w, gpointer *data)
  17:	{
  18:	        printf("button3_clicked():\n");
  19:	        gtk_main_quit();
  20:	}
  21:	
  22:	main( int argc, char *argv[] )
  23:	{
  24:	    GtkWidget *window;
  25:	    GtkWidget *vbox;
  26:	    GtkWidget *button1, *button2, *button3 ;
  27:	
  28:	        gtk_set_locale();
  29:	        gtk_init( &argc, &argv );
  30:	        window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
  31:	        gtk_signal_connect( GTK_OBJECT(window), "delete_event",
  32:	                            GTK_SIGNAL_FUNC(my_delete_event), NULL );
  33:	        gtk_signal_connect( GTK_OBJECT(window), "destroy",
  34:	                            GTK_SIGNAL_FUNC(my_delete_event), NULL );
  35:	        gtk_container_set_border_width( GTK_CONTAINER(window), 50);
  36:	
  37:	        vbox = gtk_vbox_new( FALSE, 0 );
  38:	
  39:	        button1 = gtk_button_new_with_label("kterm");
  40:	        gtk_box_pack_start( GTK_BOX(vbox), button1, FALSE, FALSE, 0);
  41:	        gtk_widget_show( button1 );
  42:	
  43:	        button2 = gtk_button_new_with_label("netscape");
  44:	        gtk_box_pack_start( GTK_BOX(vbox), button2, FALSE, FALSE, 0);
  45:	        gtk_widget_show( button2 );
  46:	
  47:	        button3 = gtk_button_new_with_label("quit");
  48:	        gtk_box_pack_start( GTK_BOX(vbox), button3, FALSE, FALSE, 0);
  49:	        gtk_widget_show( button3 );
  50:	        gtk_signal_connect( GTK_OBJECT(button3), "clicked",
  51:	                            GTK_SIGNAL_FUNC(button3_clicked), NULL );
  52:	
  53:	        gtk_container_add( GTK_CONTAINER(window), vbox);
  54:	
  55:	        gtk_widget_show( vbox );
  56:	        gtk_widget_show( window );
  57:	
  58:	        gtk_main();
  59:	        printf("return from gtk_main()\n");
  60:	        exit( 0 );
  61:	}
----------------------------------------------------------------------

button3_clicked() は、main() で作成する button3 が "clicked" というイ ベントを受け取った時に実行される関数である。gtk_main_quit() を呼び出し て終了している。

main() で gtk_set_locale() から gtk_container_set_border_width() までは、 gtk-hello.cと同じである。

vbox は、縦(vertical)に widget を並べるための箱である。hbox という、横 (horizontal)に並べる箱もある。

gtk_button_new_with_label() は、ラベルを張り込んだボタンを作る関数であ る。まずボタンを作り、後でラベルを張り込む方法もある。作成したボタン button1, button2, button3 をvbox の中に gtk_box_pack_start() で埋め込 んでいる。show もしている。

button3 については、"clicked" というイベントを受け取った時に button3_clicked() という関数が呼ばれるように設定している。

vbox と window を、show してイベント・ループ gtk_main() を呼んでいる。

実行例:

----------------------------------------------------------------------
% cp ~yas/syspro/window/gtk-button3.c . [←]
% cp ~yas/syspro/window/Makefile . [←]
% make gtk-button3 [←]
cc `gtk-config --cflags` -o gtk-button3 gtk-button3.c `gtk-config --libs`
% ./gtk-button3 [←]
button3_clicked():
return from gtk_main()
% []
----------------------------------------------------------------------

図? gtk-button3 の実行結果のウインドウ

図? gtk-button3 の実行結果のウインドウ

◆signal-int.c(再掲)

ウインドウのプログラムは、Unix シグナル(ソフトウェア割込み) とも関係 している部分がある。

◆GTK+に関するドキュメント

次のファイルやディレクトリにドキュメントがある。
GTK v1.2 Tutorial
/usr/share/doc/gtk+-devel-1.2.9/tutorial/gtk_tut.html
/usr/share/doc/gtk+-devel-1.2.9/examples/

■Ruby/GTKによるウインドウ・プログラミング

Ruby は、オブジェクト指向のスク リプト言語である。オブジェクト指向は、ウインドウのプログラミングに適し ている。

◆gtk-hello.rb

gtk-hello.c を Ruby で書き直したものを以下に示す。

----------------------------------------------------------------------
   1:	#!/usr/bin/ruby
   2:	#       gtk-hello.rb -- GTK+を使ってウインドウに文字列を表示するプログラム(Ruby版)
   3:	#       ~yas/syspro/window/gtk-hello.rb
   4:	#       Start: 2003/06/21 23:45:48
   5:	
   6:	require 'gtk'
   7:	
   8:	def my_delete_event()
   9:	        printf("my_delete_event()\n")
  10:	        Gtk::main_quit()
  11:	end
  12:	
  13:	def main()
  14:	        window = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL)
  15:	        window.signal_connect("delete_event") { my_delete_event() }
  16:	        window.signal_connect("destroy") { my_delete_event() }
  17:	        window.border_width = 50
  18:	
  19:	        label1 = Gtk::Label.new("hello,world")
  20:	        window.add( label1 )
  21:	        label1.show()
  22:	        window.show()
  23:	        Gtk.main()
  24:	        printf("return from Gtk.main()\n")
  25:	end
  26:	
  27:	main()
----------------------------------------------------------------------

Ruby/GTK+ のプログラムでは、ライブラリgtk を require する。 これにより、Gtk:: で始まる定数やメソッドが使えるようになる。

my_delete_event() は、(トップレベルのウインドウが) "delete_event"、ま たは、"destroy" というイベントを受け取った時に実行される関数である。 Ruby の関数定義は、def 関数名(引数..) で始まり、end で終わる。

main() は定義しない方が普通である。定義する場合は、最後に main() を呼 び出すことを忘れないようにする。

内部の処理は、C言語版とほとんど同じである。

Ruby では、変数(小文字で始まる)は、宣言しないで使ってもよい。型はない。 ローカル変数は、def と end の間で有効である。その外に、インスタンス変 数(@がついたもの)や暮す変数(@@がついたもの)がある。大文字で始まると定 数になる。

Ruby では、v.func で、変数 v が指しているオブジェクトで定義された関数 func の呼出しになる。v をつけないで単に func とすると、同じクラス内の 関数を呼ぶことにる。後ろの () はつけなくてもよい。

window.signal_connec() では、イベントが生じると{ } の中のブロックが実 行される。この中に処理を書くといちいち関数を定義しなくてもよい。

実行例:

----------------------------------------------------------------------
% cp ~yas/syspro/window/gtk-hello.rb . [←]
% ls -l gtk-hello.rb [←]
-rwxr-xr-x    1 yas      lab           603  6月 23 01:40 gtk-hello.rb
% chmod +x gtk-hello.rb [←]
% ls -l gtk-hello.rb [←]
-rwxr-xr-x    1 yas      lab           603  6月 23 01:40 gtk-hello.rb
% ./gtk-hello.rb  [←]
./gtk-hello.rb:23:in `main': Interrupt
        from ./gtk-hello.rb:23:in `main'
        from ./gtk-hello.rb:27
% []
----------------------------------------------------------------------

◆gtk-button3.rb

gtk-button3.cを Ruby で書き直したものを次のファ イルに置く。
~yas/syspro/window/gtk-button3.rb
window.show_all() は、1つひとつ show する代わりに全部まとめて show している。

◆gtk-button3.rb

◆Ruby/GTKに関するドキュメント

次のファイルやディレクトリにドキュメントがある。
README, 例題
/usr/doc/ruby-gtk-0.30/gtk/README.EXT.ja

■練習問題と課題

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

与えられたディレクトリのC言語のソース・プログラムだけを表示するシェル・ スクリプトを作りなさい。

----------------------------------------------------------------------
% ls-c ~/syspro-2003/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 などのオプションが付けられるようにしなさい。

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

ls コマンドや csh, tcsh, sh の * では、「.」から始まるファイルは表示 されない。逆に「.」から始まるファイルだけを表示する ls コマンドを 作りなさい。

----------------------------------------------------------------------
% ls-dot ~ [←]
.
..
.cshrc
.emacs
.login
% []
----------------------------------------------------------------------
ヒント:ls に -a オプションを付けると、全てのファイルを表示する。 先頭が「.」のものを抜き出す。

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

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

★練習問題 92 ディレクトリだけを表示するls

ディレクトリだけを表示する ls コマンドを作りなさい。

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

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

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

★練習問題 93 ファイルの大きさの順に表示するls

ls -l は、ファイルの名前の順に表示する。これを、ファイルの大きさの順に 表示するシェル・スクリプトを作りなさい。

----------------------------------------------------------------------
% ls-size ~yas/syspro/window [←]
-rwxr-xr-x    1 yas      lab         17045  6月 23 00:48 gtk-button3
-rwxr-xr-x    1 yas      lab         16166  6月 23 00:00 gtk-hello
drwxr-xr-x    2 yas      lab          4096  6月 23 01:25 CVS
drwx------    2 yas      lab          4096  6月 23 01:15 tmp
-rw-r--r--    1 yas      lab          1564  6月 23 00:48 gtk-button3.c
-rwxr-xr-x    1 yas      lab           910  6月 23 01:14 gtk-button3.rb
-rw-r--r--    1 yas      lab           895  6月 23 00:05 gtk-hello.c
-rwxr-xr-x    1 yas      lab           618  6月 23 01:14 gtk-hello.rb
-rw-r--r--    1 yas      lab           200  6月 23 01:25 Makefile
合計 64
% []
----------------------------------------------------------------------
ヒント:ls -l の出力を sort コマンドでソートする。 ソートは、第5フィールド(-k5)で行う。

★練習問題 94 ファイルの行数の順に表示するwc

wc コマンドは、ファイルの行数、単語数、バイト数を表示する。wc コマンド は、引数で与えられた順に表示する。これを行数の順に表示するようなスクリ プトを作りなさい。

----------------------------------------------------------------------
% wc-lines *.c [←]
            73           181          1166 pipe-rw-dup.c
            65           155          1015 pipe-rw-nodup.c
            57           161          1217 proc-uid-print.c
            50            98           793 setjmp-longjmp.c
            39           100           920 signal-int.c
            38           112           842 proc-create.c
            21            49           483 exec-date.c
            20            50           419 cont-1.c
            20            50           419 cont-0.c
            16            52           447 env-print.c
            15            57           460 arg-print.c
            14            24           267 fork-hello.c
            12            32           300 cont-2.c
% []
----------------------------------------------------------------------
この課題では、合計は表示されなくてもよい。

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

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

ps -le では、全プロセス(-e)が、サイズなどの情報も含めて(-l)表示される。

----------------------------------------------------------------------
% ps -le  [←]
  F S   UID   PID  PPID  C PRI NI  P    SZ:RSS      WCHAN TTY     TIME CMD
 39 S     0     0     0  0  39 RT  *     0:0     80268270 ?       0:40 sched 
 b0 S     0     1     0  0  39 20  *    98:42    8027eb80 ?       0:15 init 
119 S     0     2     0  0  39 RT  *     0:0     80267c30 ?       2:06 vhand 
119 S     0     3     0  0  39 RT  *     0:0     802678a8 ?       1:09 bdflush 
...
 b0 S     0 11771   684  0  60 20  * 11480:1622  8053853c ?      22:26 Xsgi 
 b0 S     0 22729   684  0  60 20  *   862:140   80538474 ?       0:00 xdm 
 90 Z  1231 24304 24302  0   0 -   -     - -            - -       0:00 <defunct> 
% []
----------------------------------------------------------------------
そのうち、メモリのサイズ(SZ)が大きいプロセスを 10 個だけ表示するシェル・ スクリプトを作りなさい。

----------------------------------------------------------------------
% ps-sz-top10 [←]
 b0 S     0 11771   684  0  60 20  * 11480:1622  8053853c ?      22:26 Xsgi 
 b0 S     0   548   547  0  60 20  *  2678:1619  805384e0 ?       0:30 jserver 
 b0 S  1231 24145 23879  0  60 20  *  2114:909   80538494 pts/1   0:01 emacs 
 b0 S     0 24250 23879  0  60 20  *  1166:653   80538538 pts/1   0:01 kterm 
 b0 S 40508 24517 24509  0  28 20  *  1044:766   8027f5a0 pts/3   0:00 mnews 
 b0 S     0 22729   684  0  60 20  *   862:140   80538474 ?       0:00 xdm 
 b0 S     0   684     1  0  60 20  *   854:75    80538500 ?       0:01 xdm 
 b0 S 40508 24509 24507  0  39 20  *   740:336   8027eb80 pts/3   0:01 tcsh 
 b0 S  1231 24251 24250  1  39 20  *   704:437   8027eb80 pts/2   0:01 tcsh 
 b0 S  1231 23879 23876  0  39 20  *   703:321   8027eb80 pts/1   0:01 tcsh 
% []
----------------------------------------------------------------------
ヒント:SZ の順(第10フィールド)に sort して head する。

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

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

★練習問題 96 バックアップ・ファイルとのdiff

emacs (mule) は、ファイルを保存する時に、1つの前のバージョンを「~」を 付けて保存する。そのようなファイルを見つけて、オリジナルのファイルと diff コマンドで比較するようなスクリプトを作りなさい。

% diff-backup *.c [←]
% []
ヒント:foreach または for 文で、引数のファイルについて、 "$file"~ のような名前のファイルが存在するかを調べる。 存在すれば、diff コマンドで表示する。

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

★練習問題 97 小文字のファイル名への変更

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


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

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

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

★練習問題 98 パタンを含むファイルの名前の表示

特定のパタンを含むファイルの名前を表示するプログラムをつくりなさい。た とえば、次の例では、printf というパタンを含むファイルの一覧を表示して いる。

% find-pattern printf *.c [←]
arg-print.c
env-print.c
signal-int.c
% []
ヒント:egrep -s で調べて、含んでいた時だけファイル名を表示する。

★練習問題 99 カウント・ダウン

秒単位でカウント・ダウンをするようなシェル・スクリプトを作りなさい。

% countdown  [←]
5
4
3
2
1
0
% []
ヒント:foreach か for のリストに数を並べる。 sleep 1 で、1ごとに止める。

余裕があれば、引数で何秒数えるかを指定できるようにしなさい。


% countdown 3 [←]
3
2
1
0
% []

★練習問題 100 ファイルのn行目からm行目までの表示

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

% show-n-m 10 20 filename [←]
ヒント:head コマンドと tail コマンドを組み合わせて使う。 head も tail も、表示する行数を与えることができる。

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

★練習問題 101 シェル・スクリプト自由課題

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

★練習問題 102 ボタンによるプログラムの実行

gtk-button3.c、または、 gtk-button3.rbを改変して、ボタンが押されたら対 応したプログラムが実行されるようにしなさい。

ヒント:

プログラムを実行するには、ライブラリ関数 system()を使うとよい。 ウインドウを開くプログラムの場合、「&」をつけて実行して終 了を待たない方が自然であろう。system() の引数は、csh ではなくsh で解釈 されるので注意しなさい。

余裕があれば、ボタンのラベルや実行すべきコマンドをファイルから読み込ん で定義できるようにしなさい。

★練習問題 103 ボタンを押された回数をカウント

gtk-button3.c、または、 gtk-button3.rbを改変して、3回ボタンが押された ら終了するプログラムを作成しなさい。

3回、^C (Control-C) が押されたら終了するプログラムは、 signal-int.cにある。

余裕があれば、引数を取るようにしなさい。また、カウント中の文字をボタン に表示するようにしなさい。

★練習問題 104 ウインドウ自由課題

その他、上であげた課題と同程度に難しい課題を自由に設定して解きなさい。 今まで作成したプログラムに、グラフィカル・ユーザ・インタフェースを付け 加えなさい。
Last updated: 2003/06/23 02:39:58
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>