解説 9月(3)

今回の学習項目です。


CGIはどんなプログラムなのか

掲示板のようなウェブ上のアプリケーションプログラムは、CGIと呼ばれるサーバー上で動作するプログラムです。 (CGIは"Common Gateway Interface"という言葉の略ですが、分かりにくいですね。) 詳しく言えば、ユーザーからの入力を得てサーバー上のプログラムが動き、その結果として生成されたウェブページを表示する、という種類のプログラムです。

今日の課題は、この仕組みを理解し、簡単なCGIのプログラムを作成できるようになることです。 具体的な最終目標は、次のように日付と時刻を表示するCGIプログラムを完成させることです。

time_now.cgi

CGIの仕組み

これから行うCGIのプログラムは、これまで作ってきたプログラムと違って、手元のパソコン上のプログラムではなく、サーバーと呼ばれる物理的にも離れた所にある別のコンピューター上で作成し、実行します。 具体的には、cgi.st.chukyo-u.ac.jpなどという名前がついたコンピューター上で実行させます。 (これはUnix系のOSであるLinuxをOSに使っているマシンです。)

自分のコンピューターからこのようなリモートの(離れた場所にある)コンピュータを使うためには、まずそのコンピューターにloginしてそこでプログラムを作成するか、自分のコンピュータ上でプログラムを作成してからリモートのコンピュータに送る(これをアップロード、と言います)必要があります。

もう一つCGIのプログラムの特徴はウェブブラウザ(IEやFirefoxなど)から実行できるという特徴です。 例えば、time_now.cgiという名前のプログラムを作ったとすると、ブラウザ上で、ウェブのページと同じように、URLでプログラムを指定することで、実行できます。 そして、実行結果をブラウザに表示させることができます。以下のプログラムは、URLを見れば分かるように、"cgi.st.chukyo-u.ac.jp"という名前のコンピュータ(サーバー)上に作られたプログラムです。

http://cgi.st.chukyo-u.ac.jp/~z189105/time_now.cgi

このようなプログラムの仕組みと作り方を実際にプログラムを作りながら学んでいきましょう。

課題1

課題1-0

まず、下のリンク先のページのように、「こんにちは、中京太郎さん。」をウェブブラウザに表示するプログラムを作ります。

以下の解説を読んでから、課題を解きなさい。

hello.cgi

このCGIプログラム hello.cgiは、次にあげる Rubyのプログラムで実現できます。

#!/usr/local/bin/ruby 
print "Content-type: text/html; charset=euc-jp\n\n"

print "こんにちは、中京太郎さん。\n"

上のプログラムの1行目(#から始まる行)は、このファイルをRubyのプログラムとして実行してもらうための仕掛けです。 これを書いておくと、次のような、

ruby hello.cgi    #hello.cgiを、Rubyのプログラムとして実行せよ。

というコマンドではなく、次のようにファイルを指定するだけで実行できるようになります。 [ファイルを指定するだけで実行するために]

実はこれだけでは動きません。次のような条件が必要です。

./hello.cgi       #hello.cgiを、ファイルに書かれている方法で実行せよ。

CGIのプログラムをブラウザから実行するときには、このような仕掛けが必要になります。 "/usr/local/bin/ruby"の部分は、このプログラムを解釈して実行するRubyの本体(インタープリターとか仮想マシンと呼ぶものです)が置いてある場所(ディレクトリー)と本体であるrubyを指定したものです。

[ディレクトリーの表記:絶対パスと相対パス]

ここでは「絶対パス」で表現しています。スラッシュ、"/"を使ったunix系のファイルシステムの表現になっているのは、LinuxOSを使っているのですから当然ですね。
絶対パスで表現した "/usr/local/bin/ruby" では、はじめの"/"が一番上の根、ルート(root)ディレクトリを表しています。
unix系OSでは、/usr/bin や /usr/local/bin というディレクトリーに、よく使われる基本的な実行プログラムを置いておくのが慣例です。そこに、どんなプログラムがあるのか、見てみると参考になるでしょう。また /bin というディレクトリもあり、ここにはもっとUNIX系OSで基本的なコマンドが置かれています。

2行目のprint文は、このプログラムをWebサーバーを通してブラウザから実行するために必要なものです。

[なぜ必要?]

この部分が実行されると、print文ですから、文字列が出力されます。 CGIのプログラムの出力となる文字列全体の先頭(最初の部分)に "Content-type: text/html; charset=euc-jp\n\n" が付くことになります。

CGIのプログラムの出力である文字列(HTML文)は、ウェブサーバーが受け取ります。(CGIプログラムを起動するのも、ウェブサーバーの役目です。) ウェブサーバーは、このような文字列がCGIの出力の先頭になければ、CGIの出力をブラウザーに送り出しません。 つまりせっかく、遠隔から、ウェブサーバーを通してCGIのプログラムが起動され、実行され、出力が行われても、この文字列がなければ、ウェブサーバーはブラウザーに取り次がないのです。

3行目が肝心のプログラムですが、文字列をprint文で書き出すだけのものですから、やっていることは簡単ですね。
このプログラムをサーバーの所定の場所におくことで実際にブラウザから実行できることを、以下で確かめましょう。

課題1-0-1

次の手順に従って、上のプログラムをCGIサーバー上に作成し、./hello.cgiというコマンドで実行しなさい。

まずTeraTermでLinuxOSにログインして、CGIサーバーに移動します。 (注意: Windowsでプログラムを作成した場合には、文字コードをEUCにしてセーブし、LinuxOSのマシンに転送してください。なお、そのプログラムをWindowsで動かすと「文字化け」が起こります。)

[ファイルをWindows PCからLinuxに転送する際の注意]
ファイルの転送には、いくつかの方法がありますが、簡単なのは、Windowsで作成するときに、ファイルをHドライブ、もしくはデスクトップに置いておくことです。 演習室のPCのHドライブは、Linuxマシンに入ったときのホームディレクトリになっています。 ですから、「転送する必要がなく」そのままLinuxから使えます。 同様に、Windowsのデスクトップは、Linuxからは ~/.NT/_Desktopというディレクトリとして見えます。 (~はLinuxではホームディレクトリをあらわします) いずれにせよ、演習室のPCを使っているときにはファイルを「転送する必要はないのです」。

ただこのようなことが可能なのは、本学部の演習室のPCだからでして、 一般には、Windows PCとLinuxとが記憶領域(ハードディスク)を共有することはありません。 その時には、WinSCPやFFFTP (どちらも、演習室のPCでは、「すべてのプログラム」→「ユーティリティ」からたどれます)のような転送ソフトを使います。

このようにOSが異なるところにファイルを転送したり、同じファイルを使う場合、「日本語コード」と「改行コード」に注意してください。日本語コードがOSで使用しているものと違う場合は「文字化け」が起こります。また「改行コード」が異なる場合は、適切な改行が行われません。演習室のPCでは、TeraPadというエディタがあり、本学科ではこのエディタを使うことを推奨しています。このエディタの「ファイル」タブから「文字/改行コード指定保存」という欄を選び、適切な日本語コードと改行コードが選ばれているかを確認できます。この機能があるということが、TeraPadの使用を推奨している理由の一つです。

cd /stud_cgi/自分のアカウント名

EmacsでCGIのファイルを作成します。

emacs hello.cgi

CGIのサーバーは、決められた拡張子のファイルだけを実行するように設定されていることが少なくありません。 ここでは作成するプログラムの拡張子は必ず.cgiとしてください。

Emacsでファイルを開いたら、上のプログラムをコピーしたものを貼り付けて、ファイルを保存してください。

Ctrl + x  Ctrl + s    #編集中のファイルを保存する

ファイルを閉じる前に、ファイルの文字コードを確認しておきましょう。Linuxサーバー上でプログラムを実行して日本語を正しく表示させるためには、ファイルの文字コードをEUC-JPにしておく必要があります。

[ファイルの文字コードを確認するには]

Emacsでファイルを開き、画面左下に表示されている3文字のアルファベットを確認してください。

ファイルの文字コードを確認

EJEと表示されているはずです。3文字目がEとなっているなら、このファイルはEUC-JPで保存されます。 CGIのファイルはLinux上で実行されますので、ファイルの文字コードはEUC-JPにしておくとよいでしょう

EJEと表示されていない人は、次の解説を参照してファイルの文字コードを変更してください。

[ファイルの文字コードを変更するには]

Emacsの画面左下に表示されている文字コードがEJS(DOS)となっている場合、そのファイルはShift_JISで保存されていることになります。Windows上で作成されたファイルをCGIサーバーに持ってくるとそのようなことが起こります。 この場合、ファイルの文字コードをEUC-JPに直す必要があります。
次のどちらかの方法で、文字コードをEUC-JPに変更してください。

[Emacsを使用してファイルの文字コードをEUC-JPに変更する]

Emacsで開いているファイルの漢字コードを変更するときは、次のようにします。

Esc x  #Escキーを押した後にxキーを押す

M-xというプロンプトがEmacsの画面の欄外(一番下)に出ますので、その後に、set-buffer-file-coding-systemと入力します。 (一見、長い文字列ですが、何文字か入力してからTabキーを使うと補完してくれますので、全部キー入力する必要はありません。)

M-x set-buffer-file-coding-system

入力したら、Enterキーを押します。すると次のように表示されます。

Coding system for saving file (default nil): 

そこで、次のようにeuc-jp-unixと入力し、Enterを押します。

Coding system for saving file (default nil): euc-jp-unix

Emacsの画面左下に表示されている文字コードがEJEに変わったことを確認します。 その後、ファイルをセーブすれば、開いているファイルがEUC-JPで保存されます。

[Linuxのnkfコマンドでファイルの文字コードをEUC-JPに変換する]

Linuxのnkfコマンドを使用して文字コードをEUC-JPに変換するときは、次のようにコマンドを入力し、EUC-JPで書かれた新しいファイルを作成します。 (Emacsなどでファイルを開いている場合は、一旦閉じてから以下の作業を行いましょう。)

nkf -ed 元のファイル名 > 新しいファイル名

その後、新しいファイルを元のファイルに上書きすれば、ファイル名を元に戻すことができます。

mv 新しいファイル名 元のファイル名

Emacsでファイルを開いて、文字コードがEUC-JPになっていることを確認しておきましょう。

[WindowsのTeraPadでファイルの文字コードをEUC-JPに変換する]

WindowsのエディタTeraPadを使用して文字コードをEUC-JPに変換するとき は「ファイル」タブから「文字/改行コード指定保存」を選び、 文字コードとしてEUC、改行コードとしてLFを指定して「保存」してください。

これで「こんにちは、中京太郎さん。」を表示するプログラムができました。 プログラムを実行できるか確かめるために、次のコマンドをLinuxOS上で、Teratermから入力し、実行してみましょう。

ruby hello.cgi

実行することができたら、今度は次のコマンドでプログラムを実行してみましょう。 ファイルの1行目に、このプログラムをRubyで実行するよう記述してありますから、先程と同じように実行されるはずです。

./hello.cgi

「許可がありません」と表示された場合は、ファイルに実行許可がなされているかどうか確かめてみましょう。 上のコマンドでプログラムを実行するためには、ファイルに実行属性を与えておく必要があります。

ls -l hello.cgi

-rwx------  1 h209XXX students 120 Oct 11 09:15 hello.cgi

ファイルの所有者の実行権限を表すxが表示されていない場合は、ファイルのパーミッションを変更する必要があります。 次のコマンドで、自分自身がhello.cgiを実行できるようにします。

chmod u+x hello.cgi   #User(自分)に実行権限を与える。
#または
chmod 700 hello.cgi

ファイルの実行属性を変更したら、もう一度次のコマンドでプログラムを実行してみましょう。

./hello.cgi

実際に実行して、動作することを確め、報告しなさい。実行結果も報告すること。

課題1-0-2

次に、今度はブラウザを起動して、そこからhello.cgiを動作させることができるか確認し、ブラウザに何が表示されたか報告しなさい。 作成したCGIプログラムのURLも報告しなさい。

http://cgi.st.chukyo-u.ac.jp/~自分のアカウント/hello.cgi

HTML文書の生成

上の練習で、CGIプログラムの基本が分かり、実際にウェブブラウザから動かすことができるCGIのプログラムが作れるようになったと思います。 上のプログラムの出力は単なる文字列ですが、CGIのプログラムの普通の使い方は、ブラウザが理解できるHTML言語で書かれた文字列を作り出すものです。
このことを次の演習課題をやることで理解しましょう。

課題1-1

課題1-1-1

次のプログラムをLinux上で実行したときにディスプレイに何が表示されるか、プログラムをていねいに追いかけて予測し、結果を報告しなさい。 次に、実際にプログラムをLinux上で実行し、その予測があっていたかどうかも報告しなさい。 間違っていたら、どこをどのように間違ったのかも報告しなさい。

#!/usr/local/bin/ruby 
print "Content-type: text/html\n\n"

i = rand(4)
colorName = ["red", "green", "blue", "yellow"]

print "<html>\n"
print "<body bgcolor=\"", colorName[i], "\">\n"
print "</body>\n"
print "</html>\n"

注意:下から3行目に(2箇所)現れている \" は、ダブルクォート( " )を文字として書き出すためのものです。 詳細は次を参照してください。

[文字列中の \" の意味]

次のような文をディスプレイに表示するプログラムを考えてみましょう。

123は数です。"123"は文字列です。

下のように書けばよいでしょうか?

print "123は数です。"123"は文字列です。\n"

" (ダブルクォート)に注目してみてください。これではいけませんね。 文字列が途中で途切れてしまうため、123は二つの文字列に挟まれた数とみなされ、実行するとエラーになってしまいます。

print "123は数です。"123"は文字列です。\n"

これを回避するために、" を文字として表示する際には、直前に \(バックスラッシュ)を書きます。

print "123は数です。\"123\"は文字列です。\n"
123は数です。"123"は文字列です。

このように、\ を直前に付けることで、" を文字列の終端としてではなく、一つの文字として扱うことができます。

このような \ の付いた文字のことをエスケープシーケンスといいます。\n(改行コード)もエスケープシーケンスの一種です。

ちなみに、\ を文字として表示させるときは \\ と書く必要があります。

print "バックスラッシュは\\と書きます。\n"
バックスラッシュは\と書きます。

課題1-1-2

上のプログラムをブラウザで動作させたときに何が起こるか確認し、報告しなさい。 ブラウザでページを表示し、何度も更新してみること。 作成したプログラムのURLも報告しなさい。


課題1-2

今日の日付と現在の時刻を表示するCGIのプログラムを作成してみましょう。

http://cgi.st.chukyo-u.ac.jp/~z189105/time_now.cgi

課題1-2-1

まず、このCGIのプログラムと同じようなHTML文書を表示するプログラムを作成してみましょう。 Linux上で実行すると、ディスプレイに次のような出力が出るようにしてください。 もちろん日付と時刻の部分は、実行するたびにその時の日付と時刻を表示するようにしてください。

<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=EUC-JP">
  <title>今日の日付と現在時刻</title>
</head>
<body>
今日は2012年9月30日です。<br>
現在の時刻は9時30分20秒です。
</body>
</html>

[現在時刻を取得するには]

現在の時刻をプログラムで得るには、次のようにTimeクラスを使用します。

t = Time.now

p t.year
p t.month
p t.day
p t.hour
p t.min
p t.sec

このプログラムを2012年9月30日9時30分45秒に実行すると次のようになります。

2012
9
30
9
30
45

[たくさんの文字列をまとめて出力する便利な方法]

print文で長い文字列をいくつも出力するようなときには、ヒアドキュメントを使うと便利です。
下の場合、<<EOS から EOS の間に書かれた文字が、そのまま文字列として扱われます。 (ヒアドキュメントで使われる記号はEOSには限定されません。大事なことは print文の<<の後の文字列から その後に出てくる「同じ」文字列との間が、そのまま文字列として扱われる、ということです。
もうちょっといえば、print文以外にも使えます。)

print <<EOS
ここに書かれた文字がそのまま出力されます。
改行コードを書かなくても改行されます。
"もそのまま表示されます。
EOS
ここに書かれた文字がそのまま出力されます。
改行コードを書かなくても改行されます。
"もそのまま表示されます。

文字列の終端を示す EOS の部分は、必ず行頭に書いてください。スペースを開けると終端とみなされません。

[変数の値を文字列に埋め込むには]

文字列の中で変数の値を展開するときには、文字列の中に #{変数名} と書きます。

name = "中京太郎"
print "こんにちは#{name}さん。\n"
こんにちは中京太郎さん。

ヒアドキュメントの中でも同様のことが行えます。また、変数だけでなく、次のようにプログラムを書くこともできます。

name = "中京太郎"

print <<EOS
こんにちは#{name}さん。
もうすぐ#{Time.now.hour + 1}時ですね。
EOS
こんにちは中京太郎さん。
もうすぐ10時ですね。

課題1-2-2

上と同じように表示することができたら、このプログラムをCGIとして実行できるようにしましょう。 ブラウザでこのプログラムを動作させて、日付と時刻が表示されることを確認し、ページを更新するたびに時刻が変化することを確認して、結果を報告しなさい。 作成したCGIプログラムのURLも報告しなさい。

ファイルの先頭に次のように書くのを忘れないでください。

#!/usr/local/bin/ruby
print "Content-type: text/html; charset=euc-jp\n\n"

プログラミングIIIのホームに戻る