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を使っている場面は少ないだろう…。

#2 [windows] 拡張子の関連付けを調べたり

「自分が使ってるWindowsが、どの拡張子にどのプログラムを関連付けしてるのか、自分で把握できてない」というのはやっぱりちょっと恥ずかしいなと思ったので調べる方法をググってみたり。たしか記憶では… assoc なるコマンドが関係していた…ような気がする…。

_コマンドプロンプト assoc - [ファイルと拡張子の関連を表示・設定する]

やはりそうだったらしい。assoc で調べられる。

とりあえずDOS窓で、.pl、.rb、.py について確認してみたりして。
> assoc .pl
.pl=APerl

> assoc .rb
.rb=Ruby200

> assoc .py
拡張子 .py のファイルの関連付けが見つかりません
えっ。.py は何も関連付けしてなかったのか…。 *1

APerl とか Ruby200 って何だろう。おそらく、アプリケーションの種類を示す何かだろう…。それぞれどんなアプリケーションが割り当てられているかは、ftype で調べることができる模様。

_コマンドプロンプト ftype - [拡張子に関連付けられているアプリケーションを設定する]

DOS窓で打ち込んで調べてみる。
C:\home> ftype APerl
APerl="C:\Perls\aperl589build827\bin\perl.exe" "%1" %*

C:\home> ftype Ruby200
Ruby200="C:\Ruby\Ruby200\bin\ruby.exe" "%1" %*
APerl は ActivePerl に関連付けしてあった模様。Ruby200は…コレ、Ruby 2.0.0 に関連付けしてあるのか…。古過ぎる…。まあ、ActivePerl もバージョンが古いまま使ってるのでアレなんだけど。

ところが、自分の環境において、DOS窓上で直接 .rb を実行してみると、違うバージョンのRubyが使われるようで。

以下のようなスクリプトを書いて実行してみると…。
#!ruby -Ks

puts RUBY_VERSION
> showver.rb
2.4.3

> ruby --version
ruby 2.2.6p396 (2016-11-15 revision 56800) [i386-mingw32]

> ruby showver.rb
2.2.6
assoc や ftype で確認した際には、.rb に Ruby 2.0.0 が関連付けされてるように見えたけど、.rb を直接実行してみると Ruby 2.4.3 が使われてしまう。何故。

どうやら、エクスプローラ上でダブルクリックした時の関連付けでは、Ruby 2.4.3 になっているから、っぽいなと。 *2 自分の環境では、エクスプローラ上で、.rb、.rbw は、Ruby 2.4.3 に関連付けされていて、assoc や ftype で調べたソレとは違う関連付けになっている。更に、.py、.pyw は、pythonw.exe に関連付けされてるように見えたけど、ダブルクリックして見ると IDLE が起動するという…。何故。

どうやら Windowsは、関連付けに関して、3〜4種類の設定があるようで。

_ASCII.jp:最新のWindowsにおけるファイルの関連づけを分析する (2/2)|Windows Info
_Windowsの関連付けを決めるレジストリの優先順位とassoc ftypeが効かない問題について - イム日記
_レジストリの直接編集によるファイルの拡張子と関連づけ - Glamenv-Septzen.net

ややこしいな…。関連付けにも優先順位があるらしい…。

自分の環境の場合、.rb については、 になっていた。上記レジストリの内容が assoc で確認できる内容よりも優先されるので、DOS窓上で .rb を直接実行すると、Ruby 2.0.0 ではなく Ruby 2.4.3 が呼び出されるのだろう。

更に、環境変数PATHには、Ruby 2.2.6 のインストールフォルダが登録されているので、ruby.exe を実行した際は 2.2.6 が使われる。だから、hoge.rb を直接実行した場合と、ruby hoge.rb で実行した場合で、異なるバージョンのRubyが使われるわけで。ややこしい。

とまあ、このように面倒臭いので、「ruby hoge.rb」「python hoge.py」の形で呼ぶことにしよう、.rb や .py を直接実行するのはやめておこう、とルールを決めて使っていたりするのでした…。
*1: おそらくPythonのインストール時、色んなバージョンをインストールすることが前提だったから、「関連付けする?」のチェックボックスを外した状態でインストールしたのだろう。
*2: Windows10 バージョン 1803 の場合、スタートボタン → 設定 → アプリ → 既定のアプリ → ファイルの種類ごとに既定のアプリを選ぶ、で関連付けを確認できる…のかと思ったけれどコレもちょっと不十分らしい。

#3 [ruby] Ruby2.4、2.5をWindows上にインストール

したとメモ。環境は Windows10 x64。インストールした版は、以下の2つ。
_RubyInstaller for Windows

どの時期から導入されたのか分からないけど、今現在の RubyInstaller は、DevKit 付きの版と付いてない版の2つが配布されているっぽい。以前は MSYS2 のインストールが必要だった記憶があるのだけど、これで導入が随分簡単になった、ような気がする。もっとも、DevKit と呼んでるけど実際は MSYS2 が入るようだけど。

今回は DevKit付き版をインストール。インストールしたフォルダの中を覗いてみると、msys32 というフォルダがあって、中に MSYS2 関係のファイルがごっそり入ってた。Rubyインストールフォルダ全体で、950MB前後。Ruby2.3以前と比べたらファイルサイズは大きいけど、別途MSYS2をインストールした場合より小さくなっている、ような気がするけどちょっと自信無し。自分の環境は C:\msys32 も入ってるので、そっちのファイルも流用してたら総容量はちょっと分からなくなるよなと…。

拡張ライブラリがビルドできるか確認してみたいのだけど…。以前試した Nokogiri は、mingw32版用のバイナリが配布されるようになったらしくて、gem install nokogiri をしてもビルドの処理は走らなかった。ありがたや。

それはさておき、何を入れてみればビルド処理が走るのか…。

以上、1 日分です。

過去ログ表示

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