タイトルJava Watch on the X》3 - 今度こそ「JavaからAppleScript」を実行する(3)カテゴリー開発情報, Java, AppleScript, Java Watch on the X
作成日2001/11/29 1:2:1作成者新居雅行
【MDOnline読者様限定コンテンツ】
まず、外部プロセスとして動かすコマンドを変数osascriptに代入している。値は、"/usr/bin/osascript" となっているが、必要なら、ここにスイッチなどを記述してもかまわない。
そして、実際のプログラムは、java.lang.Runtimeクラスにあるexecを使う。execメソッドもさまざまなバリエーションがあるが、引数がたくさんあってそれぞれプログラムで指定する場合にはStringの配列を引数に与えるのも便利だろう。今回は、単にコマンドラインを引数に与えるシンプルなタイプのexecメソッドを使った。なお、RuntimeのインスタンスはJava VMが自前で用意してくれているのを使うので、それへの参照を得るには、クラスメソッドのgetRuntimeを使う。いずれにしても、この書き方はお決まり的な手法だ。

☆Runtimeクラスへのインスタンスを得る(Static)
 java.lang.Runtime RunTime.getRuntime();
 戻り値:Runtimeクラスのインスタンスへの参照

(以下、《》はそのクラスのインスタンスへの参照を指定することを意味する。)

☆引数に指定したコマンドなどをもとにしてプロセスを起動する
 java.lang.Process 《Runtime》.exec(String command);
 java.lang.Process 《Runtime》.exec(String[] cmdarray);
 java.lang.Process 《Runtime》.exec(String command, String[] envp);
 java.lang.Process 《Runtime》.exec(String[] cmdarray, String[] envp);
 java.lang.Process 《Runtime》.exec(String[] cmdarray, String[] envp, File dir);
 戻り値:起動したプロセスを参照するProcess
 引数:command:プロセスを起動するコマンドライン
    cmdarray:文字列の配列で要素を合成してコマンドラインを形成
    envp:起動するプロセスに与える環境変数
    dir:プロセスの作業ディレクトリを指定する
 例外:java.io.IOException:入出力でのエラー発生時

なお、execでのコマンドラインは、Terminalのコマンドラインとはまったく同一ではないことに注意しよう。たとえば、Terminalのコマンドラインだと、> によってリダイレクトさせることができるが、execの引数に > があると、それ自体を引数ないしはパラメータと認識してしまう。コマンドが思った通りに動かない場合には、そうした点もチェックが必要だ。
execで指定するコマンドの引数には、日本語をそのまま指定するのもアリのようだ。ただ、必ずしもOKかどうかは難しい。実例で示そう。まず、以下のプログラムは動く。もちろん、引数に指定したテキストファイルは存在しており、TextEditでそのファイルを開くかどうかで実行できたかどうかを判断した。(例外処理は省略)

Runtime.getRuntime().exec("open -e ~/日本語のファイル名.txt");

ところが、以下のプログラムはきちんと動かなかった。指定のファイルは開かずに、単にTextEditが起動しただけとなった。

String cmdarray[] = {"open", "-e", "~/日本語のファイル名.txt"};
Runtime.getRuntime().exec(cmdarray);

しかしながら、以下のプログラムは動く。いずれにしてもシェルによって展開される情報については要注意であることは確かである。

String cmdarray[] = {"open", "-e", "/Users/msyk/日本語のファイル名.txt"};
Runtime.getRuntime().exec(cmdarray);

execメソッドの戻り値はProcessクラスのオブジェクトへの参照となる。実際には、Processクラスを継承したクラスとなるのだが、プログラマにとって使えるのはjava.lang.Processクラスということになる。Process Viewerを起動した状態でデバッガでステップ動作をすれば分かるが、execメソッドにより、osascriptを実行するプロセスが起動するのが見えるだろう(もっとも、Terminalでtopメニューの方が手軽かもしれない)。つまり、Processのインスタンスは実際に実行しているプロセスと見ていいわけだ。
ここでは、AppleScriptRunnerクラスを実行しているプロセスがosascriptのプロセスを起動したことになる。これらのプロセス間での標準入出力やエラー出力を使った情報のやりとりは、以下のように、それぞれのストリームを得て行うことができる。ストリームの処理については、次回以降のJava Watch on the Xで紹介する。

☆プロセスからの標準出力を受け取るストリーム
 java.io.InputStream 《Process》.getInputStream();
 戻り値:標準出力からの出力を受け取るインプットストリーム

☆プロセスからのエラー出力を受け取るストリーム
 java.io.InputStream 《Process》.getErrorStream();
 戻り値:エラー出力からの出力を受け取るインプットストリーム

☆プロセスの標準入力で送出するストリーム(Static)
 java.io.OutputStream 《Process》.getOutputStream();
 戻り値:標準入力へ入力するアウトプットストリーム

以上のメソッドの名前が分かりにくいが、命名はプロセスを起動した側のプロセスを基準にしていると思えば良く、結果的には処理対象となるProcessクラスのインスタンスから見れば、入力と出力が逆になる。
ここでは、スクリプトのプログラムを、osascriptのプロセスの標準入力に送り込みたいので、getOutputStreamで得られたOutputStreamに対して、スクリプトプログラムのテキストを送りだせばよい。ちなみに、writeメソッドで書き出しを行い、closeメソッドでストリームの終了を指定する。writeメソッドの引数はbyte型配列でないといけないので、String型からgetBytesメソッドでbyte型配列に変換している。なお、closeメソッドを実行した後ただちにスクリプトの実行が行われて、スクリプトの処理が終われば、osascriptのプロセスは終了してしまう。その後に、標準出力やエラー出力からのデータを得ることも可能である。
InputStreamからの入力はreadメソッドを使うが、まずここでは得られたデータをbyte型配列に入れることにする。byte配列として1024バイトの領域を確保し、readメソッドで、この配列にいったん取り込む。readメソッドの戻り値は、取り込んだバイト数であるが、もう取り込むデータがないときには-1を戻すので、すべてのデータを取り込んで、StringBufferに溜め込むようにループを回している。AppleScriptの処理結果やエラーくらいだとたぶん1024バイトもbyte配列を用意しておけば、1回のreadメソッドで全て読み込むだろうけど、いちおうこのあたりはまじめにプログラムを作ってみた。
(続く)
関連リンク