mieki256's diary



2018/11/10() [n年前の日記]

#1 [prog][ruby][python] Perl/Ruby/Pythonスクリプトをbatファイルにしたい

Windows10上で、Perlスクリプト、もしくはRubyスクリプトを、 _バッチファイル(batファイル) にしたいなと。

前提条件。 :

前提条件として、あらかじめWindows10に、Perl や Ruby がインストールされていることとする。

「Perl (Ruby) がインストールされているなら、batファイルなんて作らなくても、いきなりDOS窓上で hoge.pl (hoge.rb) を実行すればいいんじゃないの?」と思われるかもしれないけれど。

自分の環境では、Perl や Ruby は複数のバージョンが入っているので、.pl、.rb に一体どのバージョンが関連付けされているのか、恥ずかしながら把握できてないという…。環境変数PATHを探していって一番最初に見つかった perl.exe、ruby.exe が現在使おうとしてるソレ、というルールにして使っているので、バージョンが不明な perl.exe、ruby.exe を、いきなり呼び出されるとちょっと困る時があるわけで。

それと、今現在はどうなのか分からないけれど、Windowsには、拡張子の関連付けに基づいてPerlやRubyを起動した場合、リダイレクトやパイプが効かなくなるというバグ(仕様?)があったらしいので、batファイルにしておいたほうが確実だったりするわけで。

_「batファイルにrubyを埋め込む方法」が必要なわけ - Rubyの魔神 - はてな?Rubyグループ

Perlの場合。 :

Windows上でPerlを使う場合、 _ActivePerl をインストールして使う場面が多い(多かった)と思うのだけど。ActivePerlなら、pl2bat という、.pl を .bat に変換してくれるツールが入ってる(入っていた)。

pl2bat hoge.pl
これで、hoge.bat が作られる。

例えば、以下のようなスクリプトを書いた場合。
#!/usr/bin/perl -w
# -*- mode: perl; Encoding: sjis; coding: sjis -*-

use strict;
use warnings;

print "Hello. I am Perl";

pl2bat hoge.pl で変換すると、以下のような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 ';
#!/usr/bin/perl -w
#line 15
# -*- mode: perl; Encoding: sjis; coding: sjis -*-

use strict;
use warnings;

print "Hello. I am Perl";

__END__
:endofperl

何をやっているのかというと…。Perl は -x をつけて起動することで、読み込んだファイルに対して _shebang が出てくるまで読み飛ばす、ということができる。これにより、
  • batファイルの前半で、「perl -x -S batファイル名」を呼び出して、
  • 呼び出されたPerlは、渡されたbatファイルの後半(「#!/usr/bin/perl -w」以降の行)に書かれたPerlスクリプト部分だけを実行。
ということをしている。

_付録2
_perlrun - Perl インタプリタの起動方法 - perldoc.jp
-x directory

メールのような大きな無関係のASCII テキストのかたまりの中に プログラムが埋め込まれている事を Perl につたえます。
最初の #! で始まり、"perl" という文字列を含む行までの、 先行するゴミは捨てられます。
その行にある意味を持つスイッチは適用されます。
directory が指定されると、Perl はプログラムの実行前に、 そのディレクトリに移ります。
-x スイッチは先行するゴミの処分を制御するだけです。
プログラムの後に無視すべきゴミがある場合には、 __END__ でプログラムを終了する必要があります
(その、後に続く ゴミの一部または全部は、必要に応じて DATA ファイルハンドルを通して、 そのプログラムで処理する事ができます)。

perlrun - Perl インタプリタの起動方法 - perldoc.jp より


ついでに説明しておくと…。
  • perl に渡している -S は、環境変数PATH からスクリプトファイルを探す指定。
  • batファイル中で %0 と書くと、その部分は、実行ファイル名で置き換えられる。
  • %* と書くと、batファイルに与えられた引数全てで置き換えられる。
  • if "%OS%" == "Windows_NT" goto WinNT で、Windows NT系のOSなら :WinNT に飛び、そうでなければ次の行へと進む。
  • goto endofperl で、:endofperl に飛ぶ。
  • batファイル中で存在しないコマンドを実行しようとした場合、%errorlevel% に 9009 が入るので、エラー発生時の処理が書ける。
  • Perlスクリプトが正常終了すると errorlevel には 0 が入る。0以外ならスクリプト実行中に何かエラーが発生したということ。(エラーが発生した際に 1 等を返すようにスクリプトを記述しておくのがお約束。)

_Windowsのバッチファイルの基本的な使い方 (2/4):Tech TIPS - @IT
_Windowsバッチファイル引数 CapmNetwork
_Windowsコマンドでファイル名だけ/パスだけ取得する - 帰りは気楽な歌で

全然関係ないけど、今現在の ActivePerl って64bit版しかなくて、32bit版は入手できなくなってたのだな…。というか、今時 Windows上でPerlを使いたい場合、何を使うのが主流なのだろう。 _Strawberry Perl かな。

Rubyの場合。 :

Ruby の場合も、Perlと同様に -x オプションが用意されているので、似たようなことができる。

_Rubyスクリプトであり、バッチファイルでもあるファイルの作り方 - Route 477(2007-11-27)
_「batファイルにrubyを埋め込む方法」が必要なわけ - Rubyの魔神 - はてな?Rubyグループ

例えば、次のようなRubyスクリプトを書いたとして。
#!ruby -Ks
# -*- mode: ruby; coding: sjis -*-

puts "Hello. I am Ruby."

ファイルの最初と最後に何行か追加するだけで、batファイルとして実行できるようになる。
@echo off
ruby -x "%~f0" %*
goto endofruby

#!ruby -Ks
# -*- mode: ruby; coding: sjis -*-

puts "Hello. I am Ruby."
__END__
:endofruby

> ruby rb2bat_test.rb
Hello. I am Ruby.

> rb2bat_test.bat
Hello. I am Ruby.

Ruby に渡している "%~f0" は、実行ファイル自身を完全修飾パス名に展開したもの。例えば以下のようなbatファイルを作成して実行してみれば動作確認ができる。
echo "%~f0"

D:\temp> cat test.bat
echo "%~f0"

D:\temp> test.bat

D:\temp> echo "D:\temp\test.bat"
"D:\temp\test.bat"
ドライブ名から始まる、ファイルの完全なパスに置き換わってることが分かるかな、と。

Pythonの場合。 :

ついでに、Pythonでも似たようなことができるのかどうかを調べてみたり。Pythonの起動オプションに -x があったので、もしかして、と。

_1. コマンドラインと環境 - Python 3.7.1 ドキュメント
-x

Unix 以外の形式の #!cmd を使うために、ソースの最初の行をスキップします。これは、DOS専用のハックのみを目的としています。

1. コマンドラインと環境 - Python 3.7.1 ドキュメント より


試してみたけど、Pythonの -x オプションは、Perl や Ruby のソレとは違う動作をするので、似たようなノリでbatファイルにすることはできなかった。どうやら、本当にソースの最初の行だけをスキップするように見える…。Perl や Ruby のように、shebang が出てくるまでゴミ(?)を読み飛ばすわけではないようで。

ただ、py2bat でググったら、以下のやり取りに遭遇。2000年頃のやり取りらしい。

_py2bat (was: How do I make a Python .bat executable file?)

記述されていたスクリプトを少し手直しして試してみた。

_py2bat.py

例えば以下のようなスクリプトを書いて。

_hello.py
#!python
# -*- mode: python; Encoding: utf-8; coding: utf-8 -*-

print("Hello. I am python.")

py2bat.py で変換してみる。
python py2bat.py hello.py

hello.bat が作成された。中身は以下のような感じに。

_hello.bat
@echo off
rem="""
if "%OS%" == "Windows_NT" goto WinNT
python -x "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofpython
:WinNT
python -x "%0" %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofpython
if %errorlevel% == 9009 echo ERROR: You do not have Python in your PATH.
goto endofpython
rem """

# python script starts here
#!python
# -*- mode: python; Encoding: utf-8; coding: utf-8 -*-

print("Hello. I am python.")

# end of python script
rem="""
:endofpython
rem """

batファイルとして見た場合、Perl や Ruby と同様に、goto を使ってPythonスクリプト部分には処理が行かないように避けているのが分かる。

Pythonから、このファイルをスクリプトファイルとして見た場合は…。
  • -x オプションをつけて呼んでいるから、1行目の @echo off は無視される。
  • batファイルとして使われる部分は、Python から見て、remという変数に文字列を代入する形にしているので、Pythonスクリプトの動作には絡んでこない。(rem=""" 〜 rem """ までは、Pythonにとっては文字列の代入になる。)

なかなかトリッキーだなと…。一応動くけど…。
> python hello.py
Hello. I am python.

> cat hello.py
#!python
# -*- mode: python; Encoding: utf-8; coding: utf-8 -*-

print("Hello. I am python.")

> python py2bat.py hello.py
generating hello.bat

> hello.bat
Hello. I am python.

Pythonの場合その2。 :

前述のやり取りを追っていったら、もっと簡単なやり方も紹介されていた。

_py2bat (was: How do I make a Python .bat executable file?)

「Pythonスクリプトの一行目に以下を追加するだけでイケるんじゃね」と言っている。
@python -x %~f0 %* & goto :EOF

batファイルにとっては意味のある行だけど、Pythonから見ると -x オプションをつけて呼んでいるから、1行目は無視されてPythonの動作には無関係な行になる。

また、batファイルは暗黙的に、ファイルの最後に「:eof」というラベルがあるものとして扱われるので、goto :EOF でファイルの最後まで飛ぶ、という指定になるらしい。

_Windowsのバッチファイルの基本的な使い方 (4/4):Tech TIPS - @IT
また暗黙のラベルとして、バッチファイルの最後には「:eof」というラベルがあるものと想定されており(EOFはファイル終了、End Of Fileの略)、「goto :eof」とするとバッチファイルが終了する(「goto eof」ではなく「goto :eof」にしないとエラーになる。「help goto」参照)。

Windowsのバッチファイルの基本的な使い方 (4/4):Tech TIPS - @IT より


更に、batファイル内で「&」を使うと、左のコマンドを実行後に右のコマンドを実行、という指定になるようで。コレを使えば複数の指定を一行で書ける。

_「&」 - DOS コマンド一覧 - Programming Field
<command1> を実行した後、その結果に関わらず <command2> を実行します。
ただし、<command1> がExitなどでプロンプト(セッション)を終了させた場合は実行されません。
「&」を使用した場合、Errorlevel(または環境変数ERRORLEVEL)の値は <command2> の実行結果(& で複数つないだ場合はその最後のコマンド)による値となります。

「&」 - DOS コマンド一覧 - Programming Field より


つまり、前述のPythonスクリプトは、以下のように書けばbatファイルにできる。
@python -x "%~f0" %* & goto :EOF
#!python
# -*- mode: python; Encoding: utf-8; coding: utf-8 -*-

print("Hello. I am python.")
随分と簡単になった。

ただし、Windows95/98では動かない等の注意点もあるらしい。でもまあ、今時、NT系ではないWindowsを使っている場面は少ないだろう…。

以上です。

過去ログ表示

Prev - 2018/11 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project