Windows のバッチファイルの中に書かれた Perl スクリプトを実行する
さて、Windows 環境で Perl と仲良くする!仲良くなりたい!って昨今です。
まぁ、仲良くする目的は仕事を楽にして twitter 眺める時間を捻出するとか、早く寝るとか、そういう真の目的のためですが・・・
前回は「バッチファイルから Perl を呼び出す」ってのやりました。
そういえば
Perl 入学式でおなじみの Web フレームワークである Mojolicious::Lite ですが、Windows 環境にインストールして利用することも可能です。
もちろん、試験用のサーバ morbo
も Windows で動きます。
そして、この morbo はバッチファイル morbo.bat なのです!
where コマンドで morbo の場所を探すとこんな感じです。
C:\Users\sironekotoro>where morbo C:\Strawberry\perl\bin\morbo C:\Strawberry\perl\bin\morbo.bat
ほんで、この C:\Strawberry\perl\bin\morbo.bat
をメモ帳で開いてみると・・・最初の十数行こそバッチファイルですが、途中から Perl がそのまま書かれてますね!
コードはこちら、クリックで展開
@rem = '--*-Perl-*-- @set "ErrorLevel=" @if "%OS%" == "Windows_NT" @goto WinNT @perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9 @set ErrorLevel=%ErrorLevel% @goto endofperl :WinNT @perl -x -S %0 %* @set ErrorLevel=%ErrorLevel% @if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl @if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH. @goto endofperl @rem '; #!perl #line 16 use Mojo::Base -strict; use Mojo::Server::Morbo; use Mojo::Util qw(extract_usage getopt); getopt 'b|backend=s' => \$ENV{MOJO_MORBO_BACKEND}, 'h|help' => \my $help, 'l|listen=s' => \my @listen, 'm|mode=s' => \$ENV{MOJO_MODE}, 'v|verbose' => \$ENV{MORBO_VERBOSE}, 'w|watch=s' => \my @watch; die extract_usage if $help || !(my $app = shift); my $morbo = Mojo::Server::Morbo->new; $morbo->daemon->listen(\@listen) if @listen; $morbo->backend->watch(\@watch) if @watch; $morbo->run($app); =encoding utf8 =head1 NAME morbo - Morbo HTTP and WebSocket development server =head1 SYNOPSIS Usage: morbo [OPTIONS] [APPLICATION] morbo ./script/my_app morbo ./myapp.pl morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl morbo -w /usr/local/lib -w public -w myapp.conf ./myapp.pl Options: -b, --backend <name> Morbo backend to use for reloading, defaults to "Poll" -h, --help Show this message -l, --listen <location> One or more locations you want to listen on, defaults to the value of MOJO_LISTEN or "http://*:3000" -m, --mode <name> Operating mode for your application, defaults to the value of MOJO_MODE/PLACK_ENV or "development" -v, --verbose Print details about what files changed to STDOUT -w, --watch <directory/file> One or more directories and files to watch for changes, defaults to the application script as well as the "lib" and "templates" directories in the current working directory =head1 DESCRIPTION Start L<Mojolicious> and L<Mojolicious::Lite> applications with the L<Mojo::Server::Morbo> web server. =head1 SEE ALSO L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>. =cut __END__ :endofperl @set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%
ほほう!興味深い!!そしてわけわからん!!!
いや、うっすらはわかるけど、気になる、ってことでやっていきますよ。
何をやっていくって? 1 行ごとにコメントつけるんだよ!
頭の良い人はスラスラ推測つけて(しかもその推測あたってる)読み飛ばしていけるんだろうけど、うちはこう、アレがナニでダメな人なので・・・オランダ語の医学書を解読した江戸期の人々みたいにやっていきます。
やっていく
@rem = '--*-Perl-*--
1 行目はバッチファイルのコメント行ですね。
ちなみに、先頭に @
つけるとコマンドプロンプトで結果が非表示になります。すっきり。でもデバッグの時は外すとよいです。
@set "ErrorLevel="
続いて、環境変数を宣言。=
イコール 込みの変数名?ってなんか変ですね
@if "%OS%" == "Windows_NT" @goto WinNT
実行環境の OS の種類を判別して、"%OS%" == "Windows_NT"
であれば goto でラベル WinNT
に飛ばしています。
なお、Windows10 で実行すると Windows_NT
なります。なので、Windows10 環境だとここからラベル WinNT
まで飛びます
C:\Users\sironekotoro>echo "%OS%" "Windows_NT"
うちが動かしているのは Windows10 上なので、以下の 3 行は関係ないのですが念のため・・・
@perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
Perl を -x
と -S
のコマンドラインオプションをつけて、バッチファイルの引数のプレースホルダ 10 個つけて起動してます。
では -x
とは?ここは公式の日本語化をやってる perldoc.jp さんから
-x directory メールのような大きな無関係のASCII テキストのかたまりの中に プログラムが埋め込まれている事を Perl につたえます。 最初の #! で始まり、"perl" という文字列を含む行までの、 先行するゴミは捨てられます。 その行にある意味を持つスイッチは適用されます。 directory が指定されると、Perl はプログラムの実行前に、 そのディレクトリに移ります。 -x スイッチは先行するゴミの処分を制御するだけです。 プログラムの後に無視すべきゴミがある場合には、 END でプログラムを終了する必要があります (その、後に続く ゴミの一部または全部は、必要に応じて DATA ファイルハンドルを通して、 そのプログラムで処理する事ができます)。
へー! Perl って他のテキストファイルとかに埋め込むことできるんですね!知らんかった。というか、この morbo
のバッチが確かにそうだわ。なるほどう!
つまり、ここで #!perl
から __END__
までの Perl スクリプトが動くと。
バッチファイルに書いてある Perl が動く仕組みはこれだったのね。
つぎに -S
とは?
Perl がプログラムを探すときに環境変数 PATH を参照するようにします (プログラム名がディレクトリセパレータを含むときを除きます)。
このあともすごい長い、というか濃い解説が続きますが、Windows の PATH を読んでくれる、ってことね(ざっくり理解)。
@set ErrorLevel=%ErrorLevel%
%ErrorLevel%
は直前のコマンドの実行結果がどうであったか、コマンドの終了コードを取得するもの。それを環境変数 ErrorLevel
(=
なし)に代入している。これは 2 行目で宣言した ErrorLevel=
とは異なる変数。
@goto endofperl
そして、endofperl
のラベルがあるところ(末尾から2行目)まで飛ぶ、と。
まだまだやっていく
:WinNT
WinNT
と判定された 3 行目からこのラベルに移動してくる。
@perl -x -S %0 %*
-x
と -S
は既に調べた。で、引数のところが %0 %*
と短くなってる。既に書いたけど、バッチファイル中の Perl をここで実行する。
@set ErrorLevel=%ErrorLevel%
ここも前述なので飛ばす。と言いつつ書くと、上の perl -x -S ...
の終了コードを代入している。
morbo は ctrl - c
で止めるけど、その終了コードをここで拾っているってことなのかな?
@if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl
この %COMSPEC%
はコマンドプロンプト の本体、 cmd.exe
の本体パスとのこと。
if NOT
なので、標準のパス以外だったら終了のラベル endofperl
に飛ばしてる。
@if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH.
Perl を実行した結果の終了コードが 9009
の場合には Perl が入っていないと判断する、ってのはわかる。
ググってみたりすると、 9009
ってのはファイルが見つからない時のエラーコードらしい。ほうほう。
@goto endofperl
エラーが出たら、endobperl
ラベルへ〜
@rem ';
コメントで ;
のみだけど、これは区切りって意味かな?
で、ここからは Perl 。引数を受け取って、morbo
を起動してますね。ほんで =encoding utf8
からは POD
で解説と。
#!perl #line 16 use Mojo::Base -strict; use Mojo::Server::Morbo; use Mojo::Util qw(extract_usage getopt); getopt 'b|backend=s' => \$ENV{MOJO_MORBO_BACKEND}, 'h|help' => \my $help, 'l|listen=s' => \my @listen, 'm|mode=s' => \$ENV{MOJO_MODE}, 'v|verbose' => \$ENV{MORBO_VERBOSE}, 'w|watch=s' => \my @watch; die extract_usage if $help || !(my $app = shift); my $morbo = Mojo::Server::Morbo->new; $morbo->daemon->listen(\@listen) if @listen; $morbo->backend->watch(\@watch) if @watch; $morbo->run($app); =encoding utf8 =head1 NAME morbo - Morbo HTTP and WebSocket development server =head1 SYNOPSIS Usage: morbo [OPTIONS] [APPLICATION] morbo ./script/my_app morbo ./myapp.pl morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl morbo -w /usr/local/lib -w public -w myapp.conf ./myapp.pl Options: -b, --backend <name> Morbo backend to use for reloading, defaults to "Poll" -h, --help Show this message -l, --listen <location> One or more locations you want to listen on, defaults to the value of MOJO_LISTEN or "http://*:3000" -m, --mode <name> Operating mode for your application, defaults to the value of MOJO_MODE/PLACK_ENV or "development" -v, --verbose Print details about what files changed to STDOUT -w, --watch <directory/file> One or more directories and files to watch for changes, defaults to the application script as well as the "lib" and "templates" directories in the current working directory =head1 DESCRIPTION Start L<Mojolicious> and L<Mojolicious::Lite> applications with the L<Mojo::Server::Morbo> web server. =head1 SEE ALSO L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>. =cut __END__
:endofperl
で、ここが終着点のラベル endofperl
と。最終的にはこのラベルにくる、はず?
@set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%
さーて一気にわからなくなった。
ええっと、バッチファイルでの &
、これは commandA & commandB
と書いた場合に、commandA
を実行したあとに commandB
を実行する、というイディオムらしい。
ふむふむ。
この &
の右側は _undefined_label_
これはわからんけど、定義してないラベルに @goto しろってことかな。
で、 ||
はさっきの &
とは異なって、 commandC || commandD
となり、 commandC
が 終了結果が真の場合には commandD
は実行しない、と。
@"%COMSPEC%" /d/c @exit %ErrorLevel%
で、commandC
が偽だった場合にはこれが実行される。
%COMSPEC%
はコマンドプロンプト のパスなので、そこに /d/c
オプションをつけて実行し、@exit
すると。
つまり、 commandX & commandY || commandZ
ってことかな?ええと、これは左?右?どっちから評価されるんだ?
・・・とりあえず、ながかったー!
バッチファイル の中で Perl をうごかす!
長く書いてきたのは全て自分のため・・・ですね!
ってことで、morbo.bat
参考にして作ったバッチファイルがこちらです。
@perl -x -S %0 %* @set ErrorLevel=%ErrorLevel% @if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl @if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH. @goto endofperl @rem この下からPerl ; #!perl use strict; use warnings; my $str = "hello bat file!"; print "$str\n"; print "@ARGV"; # コマンドライン引数があれば表示される # Perl はこの下の __END__ まで __END__ :endofperl @set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%
参考にしたページ
Perl については文中にページへのリンク書いてきましたが、コマンドプロンプト に関してはこちらのページを多く参考にさせてもらいました。ありがとうございました。