[Java] JDK 7u25: Solutions to Issues caused by changes to Runtime.exec

原文はこちら。
https://blogs.oracle.com/thejavatutorials/entry/changes_to_runtime_exec_problems

WindowsプラットフォームでRuntime.execに伴う問題に直面したJava開発者のために、Java engineeringチームが以下のサンプルを用意しました。

Background

JDK 7u21で、以下のメソッドに指定するコマンド文字列のデコードがより厳密になりました。
  • Runtime.exec(String)
  • Runtime.exec(String,String[])
  • Runtime.exec(String,String[],File)
詳細はJDK 7u21のリリースノートをご覧下さい。
JDK 7u21 Release Notes for more information
http://www.oracle.com/technetwork/java/javase/7u21-relnotes-1932873.html#jruntime
これが原因で、アプリケーションに問題が発生する場合があります。以下のセクションで開発者が直面した問題と解決策をご紹介します。

[注意1]
JDK 7u25では、システムプロパティ jdk.lang.Process.allowAmbigousCommands を使ってチェックプロセスを緩和することができますので、変更できないアプリケーションの回避策として有効です。回避策はSecurityManagerを使わずに実行するアプリケーションに対してのみ有効です。詳細はJDK 7u25のリリースノートをご覧下さい。
JDK 7u25 Release Notes for more information
http://www.oracle.com/technetwork/java/javase/7u25-relnotes-1955741.html
[注意2]
Windows APIのCreateProcessの呼び出しに関する詳細を知るには、以下のページをご覧下さい。
CreateProcess function (Windows)
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx

Runtime.execの呼び出し方法は2種類あります。
  1. 文字列としてコマンドを渡す場合
    Runtime.exec(String command[, ...])
  2. 文字列の配列としてコマンドを渡す場合
    Runtime.exec(String[] cmdarray [, ...] )
このセクションで説明する問題は、前者の呼び出し方法に関連します。前者の呼び出し方法を使用する場合、開発者は、コマンドは最初に実行可能ファイル名と引数の部分に分割される必要があるWindowsに対し、コマンドを「そのまま」渡すことを期待します。しかしコマンドの引数は、JavaのAPIに従って空白文字(スペース)で実行可能ファイル名と引数に分割されます。

Problem 1: "The file path for the command includes spaces"

次のような呼び出しでは、
Runtime.getRuntime().exec("c:\\Program Files\\do.exe")
引数を空白文字で分割し、以下のような文字列の配列にとして取り扱います。
c:\\Program, Files\\do.exe
処理された配列の最初の要素を実行可能ファイル名として解釈し、(存在する場合には)SecurityManagerが検証し、実行可能パスの曖昧さを避けるため、引用符(”)で囲みます。
この結果、間違ったコマンドになってしまいます。
"c:\\Program" "Files\\do.exe"
これでは動きませんね。

Solution:

ProcessBuilderもしくはRuntime.exec(String[] cmdarray [, ...] )を使うか、実行可能パスを引用符で囲みましょう。
アプリケーションコードを変更できず、SecurityManagerを使っていない場合には、Javaプロパティjdk.lang.Process.allowAmbigousCommandsの値をコマンドラインからtrueに設定することで回避できます。これによりチェックプロセスが緩和され、曖昧な入力を許可します。
-Djdk.lang.Process.allowAmbigousCommands=true

Examples:

new ProcessBuilder("c:\\Program Files\\do.exe").start()
Runtime.getRuntime().exec(new String[]{"c:\\Program Files\\do.exe"})
Runtime.getRuntime().exec("\"c:\\Program Files\\do.exe\"")

Problem 2: "Shell command/.bat/.cmd IO redirection"

以下のような暗黙のcmd.exe呼び出しでは
Runtime.getRuntime().exec("dir > temp.txt")
new ProcessBuilder("foo.bat", ">", "temp.txt").start() 
Runtime.getRuntime().exec(new String[]{"foo.cmd", ">", "temp.txt"})
次のような間違ったコマンドに解釈してしまいます。
"XXXX" ">" temp.txt

Solution:

コマンドを正しく指定するには、以下のオプションを使いましょう。
Runtime.getRuntime().exec("cmd /C \"dir > temp.txt\"")
new ProcessBuilder("cmd", "/C", "foo.bat > temp.txt").start()
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "foo.cmd > temp.txt"})
もしくは
Process p = new ProcessBuilder("cmd", "/C" "XXX").redirectOutput(new File("temp.txt")).start();

Problem 3: "Group execution of shell command and/or .bat/.cmd files"

検証が強制されているため、以下の呼び出しの引数では間違ったコマンドを生成してしまいます。
Runtime.getRuntime().exec("first.bat && second.bat")
new ProcessBuilder("dir", "&&", "second.bat").start()
Runtime.getRuntime().exec(new String[]{"dir", "|", "more"})

Solution:

コマンドを正しく指定するためには、以下のオプションを使いましょう。
Runtime.exec("cmd /C \"first.bat && second.bat\"")
new ProcessBuilder("cmd", "/C", "dir && second.bat").start()
Runtime.exec(new String[]{"cmd", "/C", "dir | more"})
同じシナリオはcmd.exeの持つ以下の演算子"&", "||", "^"でも有効です。

Problem 4: ".bat/.cmd with special DOS chars in quoted params”

検証が強制されているため、以下の呼び出しの引数では例外を発生させてしまいます。
Runtime.getRuntime().exec("log.bat \">error<\"")
new ProcessBuilder("log.bat", ">error<").start()
Runtime.getRuntime().exec(new String[]{"log.bat", ">error<"})

Solution:

コマンドを正しく指定するには、以下のオプションを使いましょう。
Runtime.getRuntime().exec("cmd /C log.bat \">error<\"")
new ProcessBuilder("cmd", "/C", "log.bat", ">error<").start()
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "log.bat", ">error<"})

Examples:

次のようなシェルの複雑なリダイレクト
cmd /c dir /b C:\ > "my lovely spaces.txt"
は以下のようになります。
Runtime.getRuntime().exec(new String[]{"cmd", "/C", "dir \b >\"my lovely spaces.txt\"" });

The Golden Rule:

たいていの場合、 cmd.exeは2個の引数をとります。"/C"と解釈のためのコマンドです。

0 件のコメント:

コメントを投稿