Javaコードから外部コマンドを実行しようとしていますが、Runtime.getRuntime().exec(...)
とnew Process(...).start()
には違いがあります。
Runtime
を使用する場合:
Process p = Runtime.getRuntime().exec(installation_path +
uninstall_path +
uninstall_command +
uninstall_arguments);
p.waitFor();
exitValueは0で、コマンドは正常に終了します。
ただし、ProcessBuilder
の場合:
Process p = (new ProcessBuilder(installation_path +
uninstall_path +
uninstall_command,
uninstall_arguments)).start();
p.waitFor();
waitFor
は戻りますが、終了値は1001で、コマンドは途中で終了します。
ProcessBuilder
の問題を解決するにはどうすればよいですか?
Runtime.getRuntime().exec(...)
のさまざまなオーバーロードは、文字列の配列または単一の文字列を受け取ります。 exec()
の単一文字列のオーバーロードは、文字列配列を引数の配列にトークン化してから、文字列配列を受け取るexec()
オーバーロードの1つに文字列配列を渡します。一方、ProcessBuilder
コンストラクターは、文字列のvarargs配列または文字列のList
のみを受け取ります。配列またはリスト内の各文字列は個別の引数であると想定されます。いずれにしても、取得された引数は、実行のためにOSに渡される文字列に結合されます。
たとえば、Windowsでは、
Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");
与えられた2つの引数でDoStuff.exe
プログラムを実行します。この場合、コマンドラインはトークン化され、元に戻されます。しかしながら、
ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");
DoStuff.exe -arg1 -arg2
にC:\
という名前のプログラムが存在しない限り、失敗します。これは、トークン化がないためです。実行するコマンドは既にトークン化されていると想定されます。代わりに、使用する必要があります
ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe", "-arg1", "-arg2");
または代わりに
List<String> params = Java.util.Arrays.asList("C:\DoStuff.exe", "-arg1", "-arg2");
ProcessBuilder b = new ProcessBuilder(params);
Runtime.getRuntime().exec()
がStringコマンドをProcessBuilder
に渡す方法を見てください。トークナイザーを使用してコマンドを個々のトークンに分解し、ProcessBuilder
を構築するexec(String[] cmdarray, ......)
を呼び出します。
単一の配列ではなく、文字列の配列を使用してProcessBuilder
を作成すると、同じ結果が得られます。
ProcessBuilder
コンストラクターはString...
varargを受け取るため、コマンド全体を単一の文字列として渡すことは、ターミナルで引用符でコマンドを呼び出すのと同じ効果があります。
Shell$ "command with args"
はい、違いがあります。
Runtime.exec(String)
method は、コマンドと一連の引数に分割される単一のコマンド文字列を受け取ります。
ProcessBuilder
コンストラクター は、文字列の(可変引数)配列を取ります。最初の文字列はコマンド名で、残りは引数です。
したがって、ProcessBuilderに指示することは、名前にスペースやその他のジャンクが含まれる「コマンド」を実行することです。もちろん、オペレーティングシステムはその名前のコマンドを見つけることができず、コマンドの実行は失敗します。
ProcessBuilder.start()
の実装は次のとおりであるため、Runtime.exec()
とRuntime.exec()
の間に違いはありません。
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
public Process exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
だからコード:
List<String> list = new ArrayList<>();
new StringTokenizer(command)
.asIterator()
.forEachRemaining(str -> list.add((String) str));
new ProcessBuilder(String[])list.toArray())
.environment(envp)
.directory(dir)
.start();
以下と同じである必要があります:
Runtime.exec(command)
ありがとうdave_thompson_085コメント