概要
- Nim(というか普通のプロセス)からコマンドプロンプト/ターミナルを起動するのはちょっと癖がありますよ
Nimからプロセスを起動するには
Nimからプロセスを起動するには、osprocモジュール内のプロシジャ startProcess を利用します。
なので、このstartProcessに、WindowsならC:\windows\system32\cmd.exeを指定すれば、コマンドプロンプトが表示されるかと思っていたのですが、このプロシジャだとウィンドウを伴ったコマンドプロンプトが起動せず、起動元のコマンドプロンプト内でさらにコマンドプロンプトが起動されるという状況になっているようで、意図した挙動になりませんでした。
そういや、コマンドプロンプトから別のコマンドプロンプトを起動するときに、startコマンドで起動していたなと思いだし、
discard startProcess("c:\\windows\\cmd.exe", "" , @["-K","start"]))
こんなコードを実行してみたのですが、これもうまくいかず・・・
はて、どうしたもんかな~ネットをさ迷っていますと、osモジュールのexecShellCmdを使うといいよという記事を見かけ、こちらを試したところ、
discard execShellCmd("start")
意図通りの動作ができました。(やったね)
そもそも、execShellCmdってどんなソースかと見てみたところ、stdlib.hのsystem関数を呼んでいるだけでした。
proc c_system(cmd: cstring): cint {.
importc: "system", header: "<stdlib.h>".}
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [ExecIOEffect].} =
## Executes a `shell command`:idx:.
##
## Command has the form 'program args' where args are the command
## line arguments given to program. The proc returns the error code
## of the shell when it has finished. The proc does not return until
## the process has finished. To execute a program without having a
## shell involved, use the `execProcess` proc of the `osproc`
## module.
when defined(posix):
result = c_system(command) shr 8
else:
result = c_system(command)
各OSでのターミナル実行
この調子で他のOS(Mac/Linux)も調べてみたところ、結果としてこうなりました。
OS Type | execShellCmd | startProcess | 引き渡すコマンド |
---|---|---|---|
Windows | 〇 | start | |
Mac | 〇 | open Terminal.app | |
Linux | 〇 | /usr/bin/gnome-terminal |
Macの場合は、Windowsと同様にexecShellCmdでopenコマンドの引数にTerminal.app(もしくはiTerm.app等)を指定します。
片やLinuxでは、startProcessにgnome-terminalを指定すればターミナルを起動できました。さすがLinux。
まとめ
今回の調査でいろいろとNimのソースを眺めることになり、以下のことに気づきました。
- NimのAPI仕様(ドキュメント)とソースは、よく眺めたほうがいい
- Nimのコードは、そもそもC/C++にトランスパイルされるので、Cのstdlib等のAPIもしっかり押さえておいたほうがいい
- 各OS対応って意外とめんどくさい
おまけ
今回の記事ですが、こちらのツールを作っている時に調べたものです。
任意の環境変数のセットをYamlで定義しておいて、環境変数が設定済みのターミナルを起動するというツールです。
もちろん、シェルやバッチファイル等で環境変数の再定義することも承知しているのですが、各OS毎に方法が異なるのも面倒だなと思い、Dockerまでとはいかずとも、環境変数の切り替えが簡単にできるものがあったら便利かもと思い、会社の温泉開発合宿で作ってみました。
AnyEnvやDirEnvも使ってはいるのですが、AnyEnvは各バージョンのインストーラとして利用して、バージョン切り替えはこのツールを使って切り替えようかなと思っています。DirEnvは、ディレクトリに移動した時点で環境変数の書き換えがおきますが、こちらのツールは、ディレクトリ関係ないのと、Windowsでも同じように動かせるのがメリットでしょうか。
(このツールもDirEnvをWindowsで動かせないかなーという発想から作っています)
Nimであればchoosenimもありますけど、なんちゃらEnv疲れしているので、これもNimのインストーラ替わりですね。
9割9分ほど完成はしているので、興味のある方は試してみてください。
環境変数の定義(Yaml)はこんな感じになります。
- パス区切りの環境変数は、Yamlの配列表記でも記述できます
- OSにより;か:で連結されます
- 配列定義の組み立ては上から順番に実施
- (変数名)はプレースホルダで、OS依存していません。(${変数名}やら%変数名%とか書かなくても良い)
env:
JAVA_VER: 1.7.0-111
JAVA_HOME: /opt/java/(JAVA_VER)
MAVEN_HOME: /opt/maven/2.1
PYTHON_PATH: /opt/python/2.4
PATH:
- (JAVA_HOME)/bin # JAVA
- (MAVEN_HOME)/bin # MAVEN
- (PYTHON_PATH)/bin # Python
- (PATH) # PATH引き継ぎ
上記のYaml(拡張子は不要)を指定してターミナルを起動する(wsはツールの名前)
ws shell LEGACY
上記のYamlを指定して、VSCodeを起動する
ws exec LEGACY /usr/bin/code "$PWD"
あと追加したい機能として、
- ターミナルを起動する前に、任意のNimScriptを呼び出して、前処理を記述
- プレースホルダのネームスペース対応(::変数名)とか(環境名::変数名)とか
という感じで、おまけの方が本体だったのと、Nimでツール作るのは楽しいですよ。