質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.32%

perlの学習書のサンプルコードは実行できるが、僕が手入力した実行ファイルはソースがそのまま表示されて原因が分かりません

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,578

tada_tadaa

score 94

お世話になっております。
ただいまperlを学習書を用いて勉強しているのですが、学習書のサンプルコードはレンタルサーバーにアップロードして動くのですが、僕が入力して作成したperlの実行ファイルはレンタルサーバーにアップロードして、ページを表示させると、多分途中まで動いているのではないかと思いますが、HTMLを吐く部分でソースがそのまま表示されてしまいます。ちなみにこの僕が自分で作成した実行ファイルですが、レンタルサーバーではなく、自分のパソコンのLinux上ではきちんとHTMLが表示されます。

改行コードの問題かとも思いましたので、一応エディタで改行コードをLF(UNIX)で保存して、バイナリモードでアップロードしました。しかし相変わらず、ソースが表示されてしまいます。
このソースが表示されてしまうファイル名が「6-4.cgi」という名前ですが、レンタルサーバー上でこのファイルと同一ディレクトリにある「6-3.cgi」という6-4.cgiより少し、コードが少ないcgiファイルは実行できます。

パーミッションの問題かと思いましたのでパーミッションをレンタルサーバーが推奨している値に変更しても駄目でした。

以下にコードの内容を載せます。

#!/usr/bin/perl
# 僕が本を見ながら手で入力したコードでファイル名は6-4.cgi

# ===================== ユーザー設定 ==============
$CHARSET  = 'UTF-8';    #文字コード
$USERFILE = './users.dat';  #ユーザファイル
$ADMINPASS = 'goma';    # 管理パスワード

# ===================== メインプログラム ==========
loadFormdata();

if(not exists $FORM{'adminpass'}){
printGatePage();
}
elsif($FORM{'adminpass'} eq $ADMINPASS){

loadUserfile();

if($FORM{"mode"} eq "adduser"){
addUser();
}
elsif($FORM{'mode'} eq "deluser") {
deleteUser();
}
printAdminPage();
}
else{
printErrorPage("管理パスワードが違います。");
}
exit;

#======================= ユーザー追加 ==============
sub addUser
{
    my ($name, $pass) = ($FORM{'user'}, $FORM{'pass'});
    my ($salt, $saltset, $n1, $n2);

    if(exists $USERS{$name}) {
        printErrorPage("そのユーザ名はすでに存在しています。1");
    }
    $USERS{$name} = $pass;

    # パスワードの暗号化
    $saltset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
    $n1 = int(rand 64);
    $n2 = ($n1 + time) % 64;
    $salt = substr($saltset, $n1, 1) . substr($saltset, $n2, 1);
    $pass = crypt($pass, $salt);

    # ユーザーファイル書き込み
    open(FILE, ">>$USERFILE")
        or printErrorPage("ユーザーファイルが開けません。1");
    print FILE "$name:$pass\n";
    close(FILE);
}

#=========================== ユーザー削除 ==============
sub deleteUser
{
    my ($key, $name);
    foreach $key (keys %FORM) {
        if($key =~ /del_(.+)/) {
            delete $USERS{$1};
        }
    }

    # ユーザーファイル書き込み
    open(FILE, ">$USERFILE")
        or printErrorPage("ユーザーファイルが開けません。2");
    foreach $name (keys %USERS) {
        print FILE "$name:$USERS{$name}\n";
    }
    close(FILE);
}

# ===================== ユーザーファイル読み込み ==
sub loadUserfile
{
    my ($ln, $name, $pass);    

    open(FILE, "<$USERFILE")
        or printErrorPage("ユーザーファイルが開けません。3");
    while($ln = <FILE>) {
        chomp $ln;
        ($name, $pass) = split(/:/, $ln);
        $USERS{$name} = $pass;
    }
    close(FILE);
}
#======================= 管理パスワードページ出力 ==
sub printGatePage
{
#    print "aaa";
    print <<END;

Content-type: text/html;

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><title>ゆーざかんり</title></head>
<body>
<h1>ユーザ管理</h1>
<form acction="aaa" method="POST">
管理パスワード:<input type="password" name="adminpass"><br>
<input type="submit" value="決定">
</form>
</body>
</html>
END
}

#======================= ユーザ管理ページ出力 ======
sub printAdminPage
{
    my $name;

    print <<END;
Content-type: text/html; charset=$CHARSET

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><title>ゆーざかんり</title></head>
<body>
<h1>ユーザ管理</h1>
<h2>新規ユーザ登録</h2>
<form action="$ENV{'SCRIPT_NAME'}" method="POST">
ユーザ名:<input type="text" name="user"><br>
パスワード:<input type="password" name="pass"><br>
<input type="hidden" name="mode" value="adduser">
<input type="hidden" name="adminpass" value="$ADMINPASS">
<input type="submit" value="登録する">
</form>
<hr>
<h2>ユーザ削除</h2>
<form action="$ENV{'SCRIPT_NAME'}" method="POST">
<table border="1">
<tr><th>削除チェック</th><th>ユーザ名</th></tr>
END

foreach $name (sort keys %USERS) {
    print "<tr><td><input type=\"checkbox\" name=\"del_$name\"></td><td>$name</td></tr>\n";
}

print <<END;
</table>
<input type="hidden" name="mode" value="deluser">
<input type="hidden" name="adminpass" value="$ADMINPASS">
<input type="submit" value="削除する">
</form>
</body>
</html>
END
}

#============================エラーページ出力========
sub printErrorPage
{
    print <<END;
Content-type: text/html; charset=$CHARSET

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><title>ゆーざかんり</title></head>
<body>
<h1>エラー</h1>
<p>$_[0]</p>
</body>
</html>
END

    exit;
}

#========================= フォームデータ取り込み =====
sub loadFormdata
{
    my ($query, $pair);

    if($ENV{'REQUEST_METHOD'} eq 'POST') {
        read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
    }
    else {
        $query = $ENV{'QUERY_STRING'};
    }

    foreach $pair (split(/&/, $query)) {
        my ($key, $value) = split(/=/, $pair);

        $value =~ tr/+/ /;
        $value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;

        $FORM{$key} = $value;
    }
}

コード

次は学習書についてた(実行できた)サンプルコード

#!/usr/bin/perl


#============================ ユーザー設定 ====
$CHARSET   = 'Shift_JIS';    # 文字コード
$USERFILE  = './users.dat';    # ユーザーファイル
$ADMINPASS = 'goma';        # 管理パスワード


#======================== メインプログラム ====
loadFormdata();

if(not exists $FORM{'adminpass'}) {
    printGatePage();
}
elsif($FORM{'adminpass'} eq $ADMINPASS) {
    loadUserfile();

    if($FORM{"mode"} eq "adduser") {
        addUser();
    }
    elsif($FORM{'mode'} eq "deluser") {
        deleteUser();
    }
    printAdminPage();
}
else {
    printErrorPage("管理パスワードが違います。");
}
exit;


#============================ ユーザー追加 ====
sub addUser
{
    my ($name, $pass) = ($FORM{'user'}, $FORM{'pass'});
    my ($salt, $saltset, $n1, $n2);

    if(exists $USERS{$name}) {
        printErrorPage("そのユーザー名はすでに存在しています。");
    }
    $USERS{$name} = $pass;

    # パスワードの暗号化
    $saltset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
    $n1 = int(rand 64);
    $n2 = ($n1 + time) % 64;
    $salt = substr($saltset, $n1, 1) . substr($saltset, $n2, 1);
#    $pass = crypt($pass, $salt);

    # ユーザーファイル書き込み
    open(FILE, ">>$USERFILE")
        or printErrorPage("ユーザーファイルが開けません。");
    print FILE "$name:$pass\n";
    close(FILE);
}


#============================ ユーザー削除 ====
sub deleteUser
{
    my ($key, $name);

    foreach $key (keys %FORM) {
        if($key =~ /del_(.+)/) {
            delete $USERS{$1};
        }
    }

    # ユーザーファイル書き込み
    open(FILE, ">$USERFILE")
        or printErrorPage("ユーザーファイルが開けません。");
    foreach $name (keys %USERS) {
        print FILE "$name:$USERS{$name}\n";
    }
    close(FILE);
}


#================ ユーザーファイル読み込み ====
sub loadUserfile
{
    my ($ln, $name, $pass);

    open(FILE, "<$USERFILE")
        or printErrorPage("ユーザーファイルが開けません。");
    while($ln = <FILE>) {
        chomp $ln;
        ($name, $pass) = split(/:/, $ln);
        $USERS{$name} = $pass;
    }
    close(FILE);
}


#================ 管理パスワードページ出力 ====
sub printGatePage
{
    print <<END;
Content-type: text/html; charset=$CHARSET

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><title>ゆーざーかんり</title></head>
<body>
<h1>ユーザー管理</h1>
<form action="$ENV{'SCRIPT_NAME'}" method="POST">
管理パスワード:<input type="password" name="adminpass"><br>
<input type="submit" value="決定">
</form>
</body>
</html>
END
}


#================== ユーザー管理ページ出力 ====
sub printAdminPage
{
    my $name;

    print <<END;
Content-type: text/html; charset=$CHARSET

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><title>ゆーざーかんり</title></head>
<body>
<h1>ユーザー管理</h1>
<h2>新規ユーザー登録</h2>
<form action="$ENV{'SCRIPT_NAME'}" method="POST">
ユーザー名:<input type="text" name="user"><br>
パスワード:<input type="password" name="pass"><br>
<input type="hidden" name="mode" value="adduser">
<input type="hidden" name="adminpass" value="$ADMINPASS">
<input type="submit" value="登録する">
</form>
<hr>
<h2>ユーザー削除</h2>
<form action="$ENV{'SCRIPT_NAME'}" method="POST">
<table border="1">
<tr><th>削除チェック</th><th>ユーザー名</th></tr>
END

    foreach $name (sort keys %USERS) {
        print "<tr><td><input type=\"checkbox\" name=\"del_$name\"></td><td>$name</td></tr>\n";
    }

    print <<END;
</table>
<input type="hidden" name="mode" value="deluser">
<input type="hidden" name="adminpass" value="$ADMINPASS">
<input type="submit" value="削除する">
</form>
</body>
</html>
END
}


#======================== エラーページ出力 ====
sub printErrorPage
{
    print <<END;
Content-type: text/html; charset=$CHARSET

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head><title>ゆーざーかんり</title></head>
<body>
<h1>エラー</h1>
<p>$_[0]</p>
</body>
</html>
END

    exit;
}


#================== フォームデータ取り込み ====
sub loadFormdata
{
    my ($query, $pair);

    if($ENV{'REQUEST_METHOD'} eq 'POST') {
        read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
    }
    else {
        $query = $ENV{'QUERY_STRING'};
    }

    foreach $pair (split(/&/, $query)) {
        my ($key, $value) = split(/=/, $pair);

        $value =~ tr/+/ /;
        $value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;

        $FORM{$key} = $value;
    }
}

コード

具体的に何が違って自分の実行ファイルが実行できないのか(ソースが表示されてしまうのか)に興味があります。
ちなみにソースが表示されたときの画面をキャプチャしました。ソースが表示されたときの画面

ほかにも関連がありそうなこととして同一ディレクトリ内に.htaccessファイルがあります。.htaccessの内容は

Options    -Indexes
<Files users.dat>
deny from all
</Files>

です。
やはり改行コードが原因かもしれないですが、ちょっと僕のほうではエディタでLFで保存してバイナリモードでアップするくらいしか対処が思い浮かばないです。
どなたか原因を思い当たる方がいれば教えていただければと思います。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

print "Content-type: text/html; charset=utf-8\n\n";
print <<END;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
以下略
っていう風にしてみてはどうでしょうか?
なんだかContent-typeの前の改行が良くないような気がします。
または、サーバーがutf-8に対応していない可能性も考えて
print "Content-type: text/html;charset=Shift_JIS\n\n";
print <<END;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
というパターンも試してみたいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/28 02:04 編集

    回答ありがとうございます。
    oskbt様のおっしゃった方法で見事HTMLページが表示されました。ちなみに下記のようにしました。
    #======================= 管理パスワードページ出力 ==
    sub printGatePage
    {
    print "Content-type: text/html; charset=utf-8\n\n";
    print <<END;
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html>
    <head><title>ゆーざかんり</title></head>

    自分のパソコンのLinux上ではうまく動くのにレンタルサーバーでは文法的に間違ってないっぽいのに動かない原因がちんぷんかんぷんなのですが、とりあえず、
    Content-type: text/html; charset=utf-8
    はしっかり送っとけというのが今回の質問の大きな収穫だったように思います。
    どうもありがとうございました。

    追記
    >または、サーバーがutf-8に対応していない可能性も考えて
    問題が解決されたことを確認しましたし、レンタルサーバーがutf-8で動作していることは確認済みなので、Shift_JISは確認しておりません。

    キャンセル

+1

ざっと見たところでは、
print <<END;Content-type: text/html; charset=$CHARSETの間に空行があるところが問題では。

空行があるのでヘッダーが無いとみなされてHTMLとして認識されず、ヘッダー無しでHTMLがテキストファイルとして送出されてしまっているのではないでしょうか。

Content-type: text/htmlが重要で、このファイルがHTMLファイルであることをブラウザーに知らせています。
これが認識されないとHTMLとしてレンダリングされません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/28 17:36

    >レンタルサーバーのマニュアルにエラーログの出力場所が書かれているはずですので、
    >後々のために調べておいた方が良いと思います。

    問い合わせましたところ、
    use CGI::Carp qw(fatalsToBrowser);
    をすすめられました。
    ほかの方の参考になればと思いますので使い方の一例を載せときます。

    #!/usr/bin/perl

    #↓これを付け加える。
    use CGI::Carp qw(fatalsToBrowser);

    # CGIヘッダーの出力
    print "Content-type: text/html\n\n";

    # さいころを振る
    $r = rand 6;
    $d = int($r) + 1;

    # HTMLの出力
    print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n";
    print "<html>\n";
    prit "<head><title>Perlさいころ</title></head>\n";
    print "<body><p>さいころの目は<big>$d</big>です。</p></body>\n";
    print "</html>";

    ページを表示させようとすると次のようなエラーメッセージが表示されます。

    Software error:

    syntax error at 1-4.cgi line 16, near "prit "<head><title>Perlさいころ</title></head>\n""
    Execution of 1-4.cgi aborted due to compilation errors.

    ただし、今回のようなケースでは(Content-type: text/html; がうまく送られていない)、このuse CGI::Carp qw(fatalsToBrowser);を使う方法でも、レンタルサーバーのエラーページが表示されるっぽいので(僕のやり方が悪いのかもしれませんが)、特殊なケース?っていうか、Content-type: text/html; の前には空白行をいれず、ヘッダーを出力したら改行コードを続けて2つ出力するものと覚えておくことが大切であると思います。

    キャンセル

  • 2016/05/28 18:04 編集

    事後報告いただきましてありがとうございます。

    デバッグ情報をブラウザーに出力すると、万が一悪意のある第三者にサーバー内部の情報が暴露されてセキュリティー的に危険になることがありますのでご注意ください。
    あくまでデバッグ中のみオンにするようにしてください。

    なので、本番稼動中にはやはりエラーログを参照できるようにしておきたいですね。
    ロリポップはそれが無理なのかも知れませんが。

    手元にテスト用のLinux環境があるということですので、そちらを上手く活用できれば良いですね。


    それでは、アプリ開発、頑張って下さい!

    キャンセル

  • 2016/05/28 18:45

    こちらこそ、いろいろと有益な情報を教えていただきとても感謝しております。

    ロリポップは一応普通のログ?とかもあってみれるのですが、perlのエラーのログとかは見れないみたいです。PHPなんかはエラーコードをブラウザに表示させる設定があるのですが。気になられる方はお試し期間で試すなり、メールやチャットで問い合わせてみるとよいと思います。

    どうもありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.32%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Perlに関する質問
  • perlの学習書のサンプルコードは実行できるが、僕が手入力した実行ファイルはソースがそのまま表示されて原因が分かりません