ProcessBuilderとRuntime.exec()の違い


96

私はJavaコードから外部コマンドを実行しようとしていますが、Runtime.getRuntime().exec(...)との間に気付いた違いがありnew ProcessBuilder(...).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();

終了値は1001で、コマンドはwaitFor戻りますが途中で終了します。

問題を解決するにはどうすればよいProcessBuilderですか?

回答:


99

のさまざまなオーバーロードはRuntime.getRuntime().exec(...)、文字列の配列または単一の文字列を取ります。の単一文字列オーバーロードはexec()、文字列配列を引数の配列に渡す前に、文字列を引数の配列にトークン化しますexec()ProcessBuilderコンストラクタは、一方、文字列のみまたは可変引数配列取るList配列またはリスト内の各列は個々の引数であると仮定される文字列のを。どちらの方法でも、取得した引数は文字列に結合され、OSに渡されて実行されます。

たとえば、Windowsでは

Runtime.getRuntime().exec("C:\DoStuff.exe -arg1 -arg2");

DoStuff.exe2つの引数を指定してプログラムを実行します。この場合、コマンドラインはトークン化され、元に戻されます。しかしながら、

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);

それでも機能しません:List <String> params = java.util.Arrays.asList(installation_path + uninstall_path + uninstall_command、uninstall_arguments); プロセスqq = new ProcessBuilder(params).start();
ギャル

7
この文字列連結が意味をなさないなんて信じられません: "installation_path + uninstall_path + uninstall_command"。
Angel O'Sphere

8
コマンドで明示的に指定されていない限り、Runtime.getRuntime()。exec(...)はシェルを呼び出しませ。これは、最近の「Shellshock」のバグ問題に関しては良いことです。この答えは誤解を招くものです。これは、cmd.exeまたは同等の機能(UNIXでは/ bin / bashなど)が実行されると記載されているためです。代わりに、トークン化はJava環境内で行われます。
Stefan Paul Noack 2014年

@ noah1989:フィードバックをありがとう。私は答えを(うまくいけば)明確にするために更新し、特にシェルやの言及を削除しましたcmd.exe
ルークウッドワード

execのパーサーは、パラメーター化されたバージョンとまったく同じようには機能しません。そのため、数日かけて理解しました...
Drew Delano

18

Runtime.getRuntime().exec()がStringコマンドをに渡す方法を見てくださいProcessBuilder。トークナイザーを使用してコマンドを個別のトークンに分解し、exec(String[] cmdarray, ......)を構築するを呼び出しますProcessBuilder

ProcessBuilder1つではなく文字列の配列で構築すると、同じ結果が得られます。

ProcessBuilderコンストラクタはかかるString...単一のStringは、端末内に引用符でそのコマンドを呼び出すのと同じ効果がありますように全体のコマンドを渡し、可変引数を:

shell$ "command with args"

14

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


2
しかし、Qはそのメソッドを呼び出しません。それ(間接的に)呼び出しpublic Process exec(String command, String[] envp, File dir)- StringNOT String[]-呼び出しStringTokenizer、その後に(間接的に)渡された配列のプットトークンをProcessBuilder正しく7年前から3つの回答で述べたように違いをIS、。
dave_thompson_085

質問がどれだけ古いかは問題ではありません。しかし、私は答えを修正しようとします。
ユージーンロパトキン

ProcessBuilderの環境を設定できません。私は環境しか手に入れることができません...
ilke Muhtaroglu

docs.oracle.com/javase/7/docs/api/java/lang/…を参照して、環境メソッドから取得した後に環境を設定してください...
ilke Muhtaroglu

注意深く見ると、デフォルトでは環境がnullであることがわかります。
ユージーンロパトキン

14

はい、違いがあります。

  • このRuntime.exec(String)メソッドは、コマンドと一連の引数に分割する単一のコマンド文字列を取ります。

  • ProcessBuilderコンストラクタは、文字列の(可変引数)の配列をとります。最初の文字列はコマンド名で、残りは引数です。(文字列のリストを取る代替コンストラクターはありますが、コマンドと引数で構成される単一の文字列を取るコンストラクターはありません。)

したがって、ProcessBuilderに指示するのは、名前にスペースやその他のジャンクが含まれている「コマンド」を実行することです。もちろん、オペレーティングシステムはその名前のコマンドを見つけることができず、コマンドの実行は失敗します。


いいえ、違いはありません。Runtime.exec(String)はProcessBuilderのショートカットです。サポートされている他のコンストラクタがあります。
marcolopes

2
あなたは間違っています。ソースコードを読んでください! Runtime.exec(cmd)は事実上のショートカットですRuntime.exec(cmd.split("\\s+"))ProcessBuilderクラスはへの直接同等であるコンストラクタを持っていませんRuntime.exec(cmd)。これが私の答えで私が主張している点です。
スティーブンC

1
実際、次のようにProcessBuilderをインスタンス化するnew ProcessBuilder("command arg1 arg2")と、start()呼び出しは期待どおりに動作しません。おそらく失敗し、名前にスペースを含むコマンドがある場合にのみ成功します。これはまさにOPが求めている問題です!
スティーブンC
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.