WinでActive Perlを使う際の、以前から気になっていた話

例えば test.pl

#!/usr/bin/perl
while (<>) { s/hoge/moe/g; print; }

となっていて、DOSプロンプトから

>test.pl
hogehoge
^Z

とやってやった((以下、あらかじめ拡張子が.plのファイルを実行ファイルとして扱うようにWin側で設定はしてあるものとする。))ときは応答は当然 moemoe になるけれども、

>echo hogehoge | test.pl

とやってやったとき、同じく moemoe が返って来ると思いきや応答は空になる。どうやらtest.plコマンドラインから実行してやった際にパイプから何も読み込まないらしい。何でだろう。一方で

>echo hogehoge | perl test.pl

とやってやった場合には普通に moemoe を返す。

DOSプロンプトでちょこちょことフィルタなど書いてやっても、使うときにいちいち頭に perl とか打たされるのは面倒だし第一納得行かない(笑)。何とかならないかしらと思っていたら、Active Perlディレクトリに面白いサンプルを見つけた。拡張子は .bat になっていて、ファイルの頭に以下のように書いてある:

@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl
@rem ';
#!perl
(ここに普通のPerlのコードが入る)
__END__
:endofperl

自分自身を改めてPerlに食わせてやっている。-xオプションつきで呼ばれるとPerlの処理系は #!perl 行までを読み飛ばすのだそうな。結果的にPerlスクリプトとして読み込まれたときには #!perl 行から __END__ プラグマまでの間だけが実行され、一方で最初にDOSのバッチファイルとして呼ばれたときにはPerlのコードの部分は goto 文でスキップするという仕掛けで両者が同一ファイル中に共存しているらしい。


注意すべきことを一つ。上の要領で書いたPerlスクリプト部分がエラーを出した場合、エラー行の行番号は #!perl 行を1行目としてカウントする。つまり冒頭のバッチ部はカウントされない。


もう一つ面白い点。上の例で冒頭のバッチ部にコメント行 (@rem 文)が2つあるが、これのおかげでバッチ部全体がPerlのコードとして見た時にはリスト @rem への代入文の体裁になるようにしてある。Perlに読ませてやった時にはここは実行しない筈なのに何でわざわざこんなことをしているのかイマイチ謎。念の為、上の例のPerl部に print "@rem"; とか書いて保存・実行してやると答えは空。もしかしたらこれ、うっかり

>perl test2.bat

とかしてやったときに冒頭のバッチ部がエラーを出さないようにしてあるだけ?