[???] / [Java FAQ] / [S103]
S103: 外部コマンド呼出し

[Q1]
Back
どうしたら、Java プログラムから外部のコマンド(プログラム)を 呼び出せますか? [A] java.lang.Runtime クラスの exec メソッドを使います。 Runtime のインスタンスは、 スタティックメソッドの getRuntime() で取得します。 exec メソッドには、 コマンドと引数を文字列で渡すか配列で渡すかと、 環境を指定するかどうかの組み合わせで、 4つのバリエーションがあります。 状況に合わせて適当なものを選んで下さい。
[Q2]
Back
外部コマンドの入出力(標準出力、エラー出力、標準入力)は、 どうしたら得られますか? [A] 外部コマンドの標準出力の内容を得るには、 exec で得られる Process の getInputStream() を使います。 エラー出力は getErrorStream() を、 標準入力は getOutputStream() を使います。 例えば次のようにすると、標準出力が一行ずつ取り出せます。 String command = "ls -l"; Process process = Runtime.getRuntime().exec(command); InputStream is = process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; while ((line = br.readLine()) != null) { // line で何かする。 }
[Q3]
Back
Runtime#exec で外部コマンドを実行したのですが、 何も出力されません。なぜですか? [A] exec を呼ぶだけではなく、 コマンドの出力を取り出すようにプログラムする必要が有ります。 Q2 を参照して下さい。
[Q4]
Back
ファイルを操作するコマンドが正しく実行されません。 同じコマンドをシェルで実行すると動きます。なぜ? [A] 引数に `*' や `?' といったワイルドカードが含まれていませんか? ワイルドカードをシェルで解釈するシステムでは、 個々のコマンドはワイルドカードを認識しないためこの問題がおきます。 ワイルドカードを使わないようにするか、あるいは シェルを介してコマンドを実行するようにすれば解決できます。 例: String[] cmdarray = {"/bin/sh", "-c", "cp *.class dest"}; runtime.exec(cmdarray);
[Q5]
Back
Runtime#exec で外部コマンドの出力をリダイレクトしようとしているのですが、 ファイルがまったく作成されません。なぜでしょうか? [A] Runtime#exec ではリダイレクトは使えません。 出力を取り出すように書くか、 シェルを介してコマンドを実行して下さい。 出力を取り出す方法は、Q2 を参照してください。 シェルを介する方法は、次のようにします。 String[] cmdarray = {"/bin/sh", "-c", "ls > list"}; runtime.exec(cmdarray);
[Q6]
Back
Runtime#exec で DOS コマンドの dir を実行できません。 IOException が発生してしまいます。なぜ? [A] dir を exec で直接実行することは出来ません。 dir は独立したプログラム dir.exe ではなく、 command.com の内部のコマンドであるためです。 command.com の内部コマンドを利用したい場合は、 次のように /c オプションを使って実行させます。 String[] cmdarray = {"command.com", "/c", "dir"}; runtime.exec(cmdarray); 「set ...」という外部コマンドの呼び出しがNT上で動かないのも同様の理由です。
[Q7]
Back
Runtime#exec(String) で IOException が発生して、 外部コマンドが実行されません。なぜでしょうか? [A] コマンドを指定するパスに空白文字が含まれていませんか? 空白文字は引数との区切り文字に解釈されてしまいます。 代わりに Runtime#exec(String[]) を使って下さい。 [C] これは、たとえば、これ String s = "sp included/touch foo"; を、こうしろ String[] s = {"sp included/touch", "foo"}; って話ですが、なんと Windows NT では、 String[] s = {"sp", "included/touch", "foo"}; でも実行します。反則なんですが誰がいかんのでしょう...。
[Q8]
Back
MacOSにおけるRuntime#exec()の引数は? [A] Mac OSにはコマンド行がありませんので、MRJはこれ をAppleScriptの呼び出しに変換しています。アプリケーション がまったくAppleScriptに対応していない場合には、入力ファイル をオープンできません。 [j-h-b:21251]
[Q9]
Back
Process#destroy() は process を使い終わったら 呼ぶ必要が有るのでしょうか? [A] 必要有りません。 destroy() は強制的にプロセスを終了させたい場合に使います。 プロセスが既に終了している場合は呼ぶ必要は有りません。
[Q10]
Back
Process#destroy() を呼びましたが、プロセスが終了しません。なぜ? [A] 終了しない場合も有ります。 Solaris では destroy() は、プロセスに SIGTERM を送ります。 このため SIGTERM を catch するプログラムの場合には、 destroy で終了させることはできません。
[Q11]
Back
外部コマンドの終了コードは、どうしたら得られますか? [A11] Runtime#exec が返す Process の exitValue() で得られます。 なお exitValue()は、メソッド呼び出し時に 対応するコマンドが終了していないと例外を投げますから、 通常は waitFor() で終了を待つ必要が有ります。 waitFor() で止まってしまう場合には、 「Runtime#exec()で実行した外部コマンドが動いていないようです。」 を参照してみて下さい。
[Q12]
Back
Runtime#exec()で実行した外部コマンドが動いていないようです。 そのコマンドを他の方法で実行すると、きちんと動くのですがなぜでしょう? [A] Java プログラム側でコマンドの出力を読み出していないと、 バッファがいっぱいになり、外部コマンドがその出力待ちで 途中で止まることが有ります。 特に Sun の Windows版 JRE (執筆時の最新版は 1.3)では、 非常に少ない出力量でこの溢れが発生します。 Process#getInputStream() と Process#getErrorStream() で 得られるストリームからコマンドの出力を読み出すようにすることで 回避できます。一般的には、この読み出しは個別のスレッドで 行う必要が有ります。片方づつをストリームの終わりまで 読み出すのでは、読み出していない方が溢れるかもしれないからです。 [C] java.lang.Process にはこの他にもいくつかの問題があり、 Bug Id 4109888 Semantics of external process is not defined in JLS. にまとめて述べられています。
[Q13]
Back
Windows で exec で外部コマンドが起動できません。 コマンドの引数にファイル名がうまく渡せません。 [A12] コマンドのパスや引数の文字列中に、`\' が単独で含まれていませんか? Java では文字列定数中の `\' はエスケープを意味するので、 `\' を文字列中に含めたい時は `\\' と書かなければなりません。

Back
contributor: Norikazu Nakato
コメントの送り先 Java FAQ BBS