Perl 5.19.2から $!が decodeされる
ふと Perl 5.19.6をインストールして, Plackをインストールして
見るとテストがコケたので原因を調べていました. 原因は,
$!がデコードされていたことでした.
エラー
t/HTTP-Server-PSGI/harakiri.tがエラーとなります.
これには LWP::UserAgent->getが失敗することを期待する
テストが含まれています. LWP::UserAgent->getが失敗
したとき, LWP::UserAgentが内部で $!(実際は $@)を
HTTP::Message->contentに渡すのですが, HTTP::Messageは
バイト列以外を受け取ったときは dieするのでこのような
挙動となります.
% prove -bv t/HTTP-Server-PSGI/harakiri.t t/HTTP-Server-PSGI/harakiri.t .. ok 1 ok 2 ok 3 ok 4 ok 5 # check that the server is dead HTTP::Message content must be bytes at /home/syohei/perl5/perlbrew/perls/perl-5.19.7/lib/site_perl/5.19.7/LWP/UserAgent.pm line 1045. # Tests were run but no plan was declared and done_testing() was not seen. # Looks like your test exited with 255 just after 5. Dubious, test returned 255 (wstat 65280, 0xff00) All 5 subtests passed Test Summary Report ------------------- t/HTTP-Server-PSGI/harakiri.t (Wstat: 65280 Tests: 5 Failed: 0) Non-zero exit status: 255 Parse errors: No plan found in TAP output Files=1, Tests=5, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.11 cusr 0.02 csys = 0.14 CPU) Result: FAIL
エラー表示が日本語でなく, ASCIIの人はエラーは起きないものと
思います.
バイト列, 文字列の違いについては下記等を参照してください
具体例
以下のような簡単なコードを用意します.
#!perl use strict; use warnings; use 5.010; open my $fh, '<', 'not_found_file'; say $!;
Perl 5.18.1, 5.19.7でそれぞれ実行した場合, 以下のような
出力になります.
% ~/perl5/perlbrew/perls/perl-5.19.7/bin/perl test.pl Wide character in say at test.pl line 8. そのようなファイルやディレクトリはありません % ~/perl5/perlbrew/perls/perl-5.18.1/bin/perl test.pl そのようなファイルやディレクトリはありません
Perl 5.19.7では, "$!"がデコードされていることがわかります.
解決案
Perl 5.19.2以降のみを使うという場合は以下のように,
$!を encodeすればよいでしょう(Encode::encode等他の方法もあります)
#!perl use strict; use warnings; use 5.010; binmode STDOUT, ":utf8"; open my $fh, '<', 'not_found_file'; say $!;
ただこのやり方の場合, Perl 5.19.1より前のバージョンも使う場合,
2重にエンコードすることとなり, 文字化けが起こるので注意が必要です.
他には Localeを強制する等が考えられますが, 基本的には "$!"等
Localeに依存するものはあまり利用しないということでしょうか.
例えば, $!を文字列としてチェックするのではなく, 数値すなわち
errnoとしてチェックするようにする等が考えられます.
しかしどうしても使いたいというシーンもあるので, 問題がありうる
ということは頭に入れておいた方がよいかと思います.