[java] ProcessBuilder와 Runtime.exec ()의 차이점

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합니까?



답변

의 다양한 오버로드는 Runtime.getRuntime().exec(...)문자열 배열 또는 단일 문자열을 사용합니다. 의 단일 문자열 오버로드 exec()는 문자열 배열을 사용하는 exec()오버로드 중 하나에 문자열 배열을 전달하기 전에 문자열을 인수 배열로 토큰 화합니다 . 반면 ProcessBuilder생성자는 문자열의 varargs 배열 또는 문자열의 a 만 취하며 List배열 또는 목록의 각 문자열은 개별 인수로 간주됩니다. 어느 쪽이든, 얻은 인수는 실행을 위해 OS에 전달되는 문자열로 결합됩니다.

예를 들어 Windows에서는

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

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()받는 문자열 명령을 전달합니다 ProcessBuilder. 그러므로, 호출하는 토크 나이저를 사용하여 개별 토큰으로 명령을 폭발 exec(String[] cmdarray, ......)를 구성한다 ProcessBuilder.

당신이를 구성하는 경우 ProcessBuilder대신 하나 하나의 문자열 배열로, 같은 결과를 얻을 수 있습니다.

ProcessBuilder생성자는 필요 String...하나의 문자열로 전체 명령을 전달하는 터미널에서 따옴표로 그 명령을 호출하는 것과 같은 효과가 있으므로, 가변 인자를 :

shell$ "command with args"


답변

의 구현 은 다음 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 에게 감사드립니다 .


답변

네, 차이가 있습니다.

  • Runtime.exec(String)메서드 는 명령과 일련의 인수로 분할되는 단일 명령 문자열을 사용합니다.

  • ProcessBuilder생성자 문자열 (변수 인수) 배열 걸린다. 첫 번째 문자열은 명령 이름이고 나머지는 인수입니다. (문자열 목록을 사용하는 대체 생성자가 있지만 명령과 인수로 구성된 단일 문자열을 사용하는 생성자는 없습니다.)

그래서 당신이 ProcessBuilder에게 명령하는 것은 이름에 공백과 다른 쓰레기가있는 “명령”을 실행하는 것입니다. 물론 운영 체제는 해당 이름의 명령을 찾을 수 없으며 명령 실행이 실패합니다.


답변