ちょっとしたスクリプトをWindows/Linux/macOS兼用で動作させる
ちょっとそういうのが必要になったので
確か Embulk でこんな感じのことをやっていたはず…という記憶を頼りに作ってみた。 とりあえず動くけど、後述のとおり行儀のいい方法ではない&制約事項もあるので、使う場合は要注意。
環境
条件
- スクリプト内ではNode.jsで複数行のソースコードを実行したい。Node.jsはインストール済みでパスが通っている前提。
- 実行は1ファイルで完結させたい。実行時引数とかも無し。
- Windows(バッチファイル)でもLinux/macOS(bash/zsh)でも全く同じファイルを使う。
できあがったもの
こんな感じ。
: <<BAT @echo off echo ^ console.log('Node.js on Windows'); ^ let a = 1; ^ let b = 2; ^ console.log("a + b = " + (a + b)); | node.exe exit /b BAT node <<-EOS console.log('Node.js on Linux/macOS') let a = 1 let b = 2 console.log("a + b = " + (a + b)) EOS
runanywhere.bat
的な名前で保存し、各環境で実行する。
改行コードは LF
にする。
実行例
Windows(コマンドプロンプト)
C:\work> runanywhere.bat Node.js on Windows a + b = 3
Linux(bash), macOS(zsh)
$ ./runanywhere.bat Node.js on Linux/macOS a + b = 3
仕組み
スクリプトの大枠は下記のとおり。
: <<BAT バッチファイル(Windows)として動作するコード exit /b BAT シェルスクリプト(Linux/macOS)として動作するコード
<<BAT
~ BAT
にバッチファイルのコードを書いてあり、その後ろにシェルスクリプトのコードが続くという構成になっている。
シェルスクリプト(Linux/macOS)から見ると <<BAT
~ BAT
の部分はコードはヒアドキュメントとして扱われ、:
という何もしないコマンドに渡される。つまり、ただの文字列を何もしないコマンドに渡しているだけなので何も起こらず、その次に続くシェルスクリプトのコードが普通に実行される。
バッチファイル(Windows)から見ると :
は GOTO
コマンドのラベルであり、それだけでは特に何も処理は行われない。また、 : <<BAT
のような表記も構文上は問題が無い。
このため、その次に続くバッチファイルのコードが普通に実行されて、最後に exit /b
で終了する。それ以降のコードは実行されないので、後ろにシェルスクリプトのコードがあっても問題は無い。
あとは各環境でNode.jsにソースコードを渡して実行する処理を書けばOK。
シェルスクリプトの場合は普通にヒアドキュメントが書けるので、それをそのままNode.jsに流し込む。
バッチファイルの場合はヒアドキュメントが無いので、1行にまとめて標準入力経由でNode.jsにコードを渡している。
今回は読みやすさを考慮して ^
で改行しているが、実態は下記のようなコードと同じ。
echo console.log('Node.js on Windows'); let a = 1; let b = 2; console.log("a + b = " + (a + b)); | node.exe
制約事項
改行コードは LF
にする必要がある。でないとLinux/macOSでエラーになる。バッチファイル的には良くないが、だいたい動くので許容する。
# CR/LFにすると… $ ./runanywhere.bat : not foundre.bat: 12: Node.js on Linux/macOS a + b = 3
ただし、日本語(マルチバイト文字)は使えない。バッチファイルで改行コードが LF
の場合、日本語が含まれるとコマンドの一部が削られてしまい、エラーになる。
ちなみにこの挙動は マルチバイト文字を含まない場合にも発生することがあるらしい ので注意。
# こんな感じでコメントに日本語を入れてみると… : <<BAT @echo off rem 日本語 echo ^ ...
# 実行時にコマンドが削られてエラーになる C:\work>runanywhere.bat C:\work>ho off 'ho' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。 C:\work>m 日本語 'm' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。 ...
バッチファイルの部分を CR/LF
、シェルスクリプトの部分を LF
にすれば解決するはずだけど、手作業で作るスクリプトでそれを維持し続けるのはさすがに厳しいので、LF
に統一というのが落としどころだと思う。
まとめ
とりあえず動くので使えるんだけど、改行コードが LF
のバッチファイルってことであまり良くないので、多用は禁物。