WSH (Windows Script Host) のスクリプトを Windows 上の UNIX 環境である Cygwin や WSL から直接実行したい。
例えば
BAT ファイルでそういうことを行う方法がある事は、前々から把握してて例えば、
WSH batch file embed とか「バッチファイル WSH 埋め込み」とでググると参考になるページが幾つか見つかる。
基本的に、ファイル名が *.bat のバッチファイルである必要があるが、
というような方法がある。
更に、WSF (Windows Script File) と呼ばれる XML 形式のフォーマットに Job を定義して WSH の JScript や VBAScript を記述する方法を用いると、1 つのスクリプトに BAT ファイル、JScript、VBAScript を混在させた形で複数の Job を定義し、Job 内では互いのスクリプト間で関数を呼び合う事すら可能な、以下のような方法も見つけた。
JScript または VBScript 単独でシンプルに書くとこんな感じ。
Shebang 代わりの前段が少々長いのと、末尾のタグが必須なのがちょっと面倒臭いが、これはなかなか使い勝手が良い。
例えば
$ ./hello.jsだとか
$ ./hello.vbsみたいなことがしたい。
BAT ファイルでそういうことを行う方法がある事は、前々から把握してて例えば、
WSH batch file embed とか「バッチファイル WSH 埋め込み」とでググると参考になるページが幾つか見つかる。
- stackoverflow / 2012-01-31: Is it possible to embed and execute VBScript within a batch file without using a temporary file?
- Qiita /snipsnipsnip / 2020-06-24: Windowsでshebangもどき、またはバッチにスクリプトを埋め込む方法
- イネマルのプログラミング備忘録 / 2018-04-20: WSH WSF JScript・VBScript をバッチに埋め込む方法(現在時刻を読み上げるバッチファイル)
基本的に、ファイル名が *.bat のバッチファイルである必要があるが、
@if(0)==(0) ECHO OFF cscript //NoLogo //E:JScript "%~f0" %* & EXIT /B %ERRORLEVEL% @end WScript.Echo("hello");
::'<SUB>@cscript //NoLogo //E:VBScript "%~f0" & EXIT /B %ERRORLEVEL% ' 上記の "<SUB>" はファイル終端コード ^Z(= 0x1A) の意味なので、何らかの方法で ^Z を得て適当に置換すること WScript.Echo "hello"
更に、WSF (Windows Script File) と呼ばれる XML 形式のフォーマットに Job を定義して WSH の JScript や VBAScript を記述する方法を用いると、1 つのスクリプトに BAT ファイル、JScript、VBAScript を混在させた形で複数の Job を定義し、Job 内では互いのスクリプト間で関数を呼び合う事すら可能な、以下のような方法も見つけた。
<!-- : Batch File @ECHO OFF ECHO hello with Batch File cscript //NoLogo "%~f0?.wsf" //Job:job1 cscript //NoLogo "%~f0?.wsf" //Job:job2 cscript //NoLogo "%~f0?.wsf" //Job:job3 cscript //NoLogo "%~f0?.wsf" //Job:job4 EXIT /B %ERRORLEVEL% ----- WSF Script ---> <package> <job id="job1"> <script language="VBScript"> WScript.Echo "hello with VBScript" </script> </job> <job id="job2"> <script language="JScript"> WScript.Echo("hello with JScript"); </script> </job> <job id="job3"> <script language="JScript"> function hello_js() { WScript.Echo("hello with JScript in job3"); } </script> <script language="VBScript"> Sub hello_vbs() WScript.Echo "hello with VBScript in job3" End Sub </script> <script language="JScript"> hello_js(); hello_vbs(); </script> </job> <job id="job4"> <script language="JScript"> function hello_js() { WScript.Echo("hello with JScript in job4"); } </script> <script language="VBScript"> Sub hello_vbs() WScript.Echo "hello with VBScript in job4" End Sub </script> <script language="VBScript"> call hello_js call hello_vbs </script> </job> </package>
JScript または VBScript 単独でシンプルに書くとこんな感じ。
<!-- : @cscript //NoLogo "%~f0?.wsf" %* & @EXIT /B %ERRORLEVEL% --><job><script language="JScript"> WScript.Echo("hello"); </script></job>
<!-- : @cscript //NoLogo "%~f0?.wsf" %* & @EXIT /B %ERRORLEVEL% --><job><script language="VBScript"> WScript.Echo "hello" </script></job>
Shebang 代わりの前段が少々長いのと、末尾のタグが必須なのがちょっと面倒臭いが、これはなかなか使い勝手が良い。
さて、Cygwin や WSL から *.js や *.vbs を cscript.exe に食わせる方法なのだが、
ふと Cygwin や WSL では少なくとも bash の場合、実行ビットが立ってて shebang がないと、shell script 扱いして bash に食わしてる事に気付いた。
んで、BAT ファイルに WSH を埋め込む方法は基本的にバッチファイルの頭の部分を WSH のコメントに見せかけつつ、cmd.exe のコマンドとしては有効な命令とすることで、その中から cscript を呼び出すテクニックなんだけど、んじゃぁ 1 行目を WSH にコメント扱いさせて、bash 的に実行可能な構文にしてやれば、shebang 代わりになるじゃないかという事に気付いてしまったわけである。
つまり、JScript の場合は、// から始まるコマンドの絶対パス、VBScript の場合は '...' で囲ったコマンド名または '' からコマンドを始めればよいということになる。
ただし、Cygwin の場合、// で始めると UNC 扱いされてしまうため問題がある。
試してみたところ、これは、/// とすると普通にフルパスとして扱えるという事が確認出来た。
という事で、上記の思い付きを実装してみたところ、以下のようにすることで目出度く目的を達することが出来た。
ただし、このままだと、"$0" のパスが cscript.exe に解釈不能な場合があるので、cygpath, wslpath を用いて UNIX 環境 → win32 環境の path 変換をする wrapper を作り /usr/bin/env の代わりに使う方が良いと思われる。
因みに、これを思い付いたのは、WSH Wrapper なる bash script をほぼ書き終えた直後で、書いてたものがなんか無駄になってしまった感があるんだけど、割と応用は効きそうだし、上記のパス変換の問題もあるので、後日暇を見て調整してから公開したい。
以上、結果的に bash script をベースに WSH を埋め込んだのハイブリッドスクリプトが書けるねという話になった。
なお、上記ではファイル名を *.sh としているが、バッチファイルと違って、実行ビットさえ立ってればファイル名は自由なので、*.sh はおろか *.js や *.vbs すら付与する必要はないので念のため。
追記:
cygpath, wslpath への簡易対応してみた。
あと、改行コードは CR LF (0x0d 0x0a) だと動かないので LF (0x0a) にする事。
ふと Cygwin や WSL では少なくとも bash の場合、実行ビットが立ってて shebang がないと、shell script 扱いして bash に食わしてる事に気付いた。
んで、BAT ファイルに WSH を埋め込む方法は基本的にバッチファイルの頭の部分を WSH のコメントに見せかけつつ、cmd.exe のコマンドとしては有効な命令とすることで、その中から cscript を呼び出すテクニックなんだけど、んじゃぁ 1 行目を WSH にコメント扱いさせて、bash 的に実行可能な構文にしてやれば、shebang 代わりになるじゃないかという事に気付いてしまったわけである。
つまり、JScript の場合は、// から始まるコマンドの絶対パス、VBScript の場合は '...' で囲ったコマンド名または '' からコマンドを始めればよいということになる。
ただし、Cygwin の場合、// で始めると UNC 扱いされてしまうため問題がある。
試してみたところ、これは、/// とすると普通にフルパスとして扱えるという事が確認出来た。
という事で、上記の思い付きを実装してみたところ、以下のようにすることで目出度く目的を達することが出来た。
ただし、このままだと、"$0" のパスが cscript.exe に解釈不能な場合があるので、cygpath, wslpath を用いて UNIX 環境 → win32 環境の path 変換をする wrapper を作り /usr/bin/env の代わりに使う方が良いと思われる。
因みに、これを思い付いたのは、WSH Wrapper なる bash script をほぼ書き終えた直後で、書いてたものがなんか無駄になってしまった感があるんだけど、割と応用は効きそうだし、上記のパス変換の問題もあるので、後日暇を見て調整してから公開したい。
以上、結果的に bash script をベースに WSH を埋め込んだのハイブリッドスクリプトが書けるねという話になった。
なお、上記ではファイル名を *.sh としているが、バッチファイルと違って、実行ビットさえ立ってればファイル名は自由なので、*.sh はおろか *.js や *.vbs すら付与する必要はないので念のため。
追記:
cygpath, wslpath への簡易対応してみた。
///bin/true;w=( $(type -p cygpath wslpath) );cscript.exe //NoLogo "$("$w" -w "$0")" "$@";exit $? WScript.Echo("hello");
'':;w=( $(type -p cygpath wslpath) );cscript.exe //NoLogo "$("$w" -w "$0")" "$@";exit $? WScript.Echo "hello"
あと、改行コードは CR LF (0x0d 0x0a) だと動かないので LF (0x0a) にする事。
タグ
コメントをかく