ルモーリン
ホーム更新サービス雑談ランドナーコースガイド鉄ゲタ自転車Linuxリンク連絡先

エラー出力が出ない

こんなツイートを見て

エラーメッセージが表示されないと対処のしようがないので、とても興味を覚えました。

ギリで再現「しない」コード

これは無事に動作する最小限のコードです。 文字コードはutf8です。
#!/usr/bin/env perl -w

use utf8;

binmode STDERR, ":encoding(cp932)";

0 or die "無理で~す";
こういう風に表示されます。
無理で~す at sample_45.pl line 7.

再現コード

実行しても全く表示されないコードです。 差は間違いさがしレベル。 文字コードも同じです。
#!/usr/bin/env perl -w

use utf8;

binmode STDERR, ":encoding(cp982)";

0 or die "無理で~す";

まとめ(←間違い)

binmodeのencodingの中に指定する文字コード名を間違えてもbinmodeの実行は成功、 実際にエラー出力をする時点でエンコードできずに失敗、 そのエラーも出力できず、エラー出力が出ないと推測しました。

その後

元ネタのアカウントさんから連絡があり、私が症状を誤解していたので改めて挑戦しました。 アカウントさんの記事です。 週記くらい | Active Perl で binmode STDERR, “:encoding(cp932)”; 後の dieでフリーズ

症状

メッセージが表示されず、実行中のまま、プロンプトが表示されない。

再現する要素

上記の記事と同様、私の環境でも再現しました。 それからフリーズする要素を絞り込んでいきますと、
binmode STDERR, ":encoding(cp932)";
の実行が肝になっているようです。 それとdieコマンドでPerlがエラーメッセージにスクリプトのファイル名(パス含む)を追加する際は、 文字コードがcp932のまま、右から左へ(無変換で)入れているようです。 元々がcp932なのにbinmodeの指定でutf8前提でcp932に変換してしまうケースでフリーズしていました。 なので、dieでなくてもbinmode実行後に実行時エラーが発生すればパスの漢字変換に失敗してフリーズしました。 それと$SIG{__DIE__}にハンドラを指定してdieに渡る前のメッセージを見ると、 dieに指定したメッセージとperlが追加したメッセージが連結されてしまうので、 連結前に文字コードを統一しなくてはなりません。

utf8でdieできない?

STDERRをbinmodeで変更しなければ発生しませんけれど、 dieにutf8のリテラルを指定すると、 まんまコマンドプロンプトに出力しようとして文字化けしてしまいます。

utf8でdieする方法

dieする際のメッセージをcp932にエンコードすればよいけれど、毎度やるのは面倒なので。
#!/usr/bin/env perl -w

use utf8;
use Encode::Locale;
use subs "die";

die "本日は晴天なり";

exit;

sub die {
 	my $msg = Encode::encode locale => $_[0];
 	my (undef, $filename, $line) = caller;
 	print STDERR "$msg at $filename line $line.\n";
 	exit;
}

まとめ(今度こそ?)