수색…


비고

API에서는 버전 1.5에서 ProcessBuilder.start() 사용하여 프로세스를 만드는 기본 방법을 사용하는 것이 좋습니다.

또 다른 중요한 점은 waitFor 에 의해 생성 된 종료 값이 실행중인 프로그램 / 스크립트에 종속된다는 것입니다. 예를 들어, calc.exe에서 생성 된 종료 코드는 notepad.exe 와 다릅니다.

간단한 예 (Java 버전 <1.5)

이 예제에서는 창 계산기를 호출합니다. 종료 코드는 호출되는 프로그램 / 스크립트에 따라 달라질 수 있습니다.

package process.example;

import java.io.IOException;

public class App {

    public static void main(String[] args) {
        try {
            // Executes windows calculator
            Process p = Runtime.getRuntime().exec("calc.exe");

            // Wait for process until it terminates
            int exitCode = p.waitFor();

            System.out.println(exitCode);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ProcessBuilder 클래스 사용하기

ProcessBuilder 클래스를 사용하면 명령 행을 통해 명령을 쉽게 보낼 수 있습니다. 입력해야 할 명령을 구성하는 문자열 목록 만 있으면됩니다. ProcessBuilder 인스턴스에서 start () 메소드를 호출하여 명령을 실행하기 만하면됩니다.

Add.exe라는 두 개의 인수를 사용하는 프로그램이 있다면 코드는 다음과 같습니다.

List<String> cmds = new ArrayList<>();
cmds.add("Add.exe"); //the name of the application to be run
cmds.add("1"); //the first argument
cmds.add("5"); //the second argument

ProcessBuilder pb = new ProcessBuilder(cmds);

//Set the working directory of the ProcessBuilder so it can find the .exe
//Alternatively you can just pass in the absolute file path of the .exe
File myWorkingDirectory = new File(yourFilePathNameGoesHere);
pb.workingDirectory(myWorkingDirectory);

try {
    Process p = pb.start(); 
} catch (IOException e) {
    e.printStackTrace();
}

명심해야 할 몇 가지 사항 :

  • 명령 배열은 모두 문자열 배열이어야합니다.
  • 명령은 명령 행에서 프로그램을 호출 한 경우 (즉, .exe의 이름은 첫 번째 인수 뒤에 올 수 없으므로) 배열 순서대로 있어야합니다
  • 작업 디렉토리를 설정할 때 파일 이름뿐만 아니라 파일 객체도 String으로 전달해야합니다.

블로킹 호출과 비 블로킹 호출 비교

일반적으로 명령 행을 호출 할 때 프로그램은 명령을 전송 한 다음 실행을 계속합니다.

그러나 자신의 실행을 계속하기 전에 호출 된 프로그램이 끝날 때까지 기다릴 수도 있습니다 (예 : 호출 된 프로그램은 파일에 데이터를 쓰고 프로그램은 해당 데이터에 액세스해야합니다).

이것은 리턴 된 Process 인스턴스에서 waitFor() 메소드를 호출하여 쉽게 수행 할 수 있습니다.

사용 예 :

//code setting up the commands omitted for brevity...

ProcessBuilder pb = new ProcessBuilder(cmds);

try {
    Process p = pb.start();
    p.waitFor();
} catch (IOException e) {
    e.printStackTrack();
} catch (InterruptedException e) {
    e.printStackTrace();
}

//more lines of code here...

ch.vorburger.exec

원시 java.lang.ProcessBuilder API를 직접 사용하여 Java에서 외부 프로세스를 시작하는 것은 다소 번거로울 수 있습니다. Apache Commons Exec 라이브러리를 사용하면 좀 더 쉽게 사용할 수 있습니다. ch.vorburger.exec 라이브러리 는 Commons Exec을 확장하여 진정으로 편리하게 만듭니다.

 ManagedProcess proc = new ManagedProcessBuilder("path-to-your-executable-binary")
     .addArgument("arg1")
     .addArgument("arg2")
     .setWorkingDirectory(new File("/tmp"))
     .setDestroyOnShutdown(true)
     .setConsoleBufferMaxLines(7000)
     .build();

proc.start();
int status = proc.waitForExit();
int status = proc.waitForExitMaxMsOrDestroy(3000);
String output = proc.getConsole();

proc.startAndWaitForConsoleMessageMaxMs("started!", 7000);
// use service offered by external process...
proc.destroy();

Pitfall : Runtime.exec, Process 및 ProcessBuilder는 쉘 구문을 이해하지 못합니다.

Runtime.exec(String ...)Runtime.exec(String) 메소드를 사용하면 명령을 외부 프로세스로 실행할 수 있습니다 1 . 첫 번째 버전에서는 명령 이름과 명령 인수를 문자열 배열의 개별 요소로 제공하고 Java 런타임은 OS 런타임 시스템에 외부 명령을 시작하도록 요청합니다. 두 번째 버전은 사용하기가 쉽지만 몇 가지 함정이 있습니다.

우선, 안전하게 사용되는 exec(String) 을 사용하는 예제가 있습니다 :

Process p = Runtime.exec("mkdir /tmp/testDir");
p.waitFor();
if (p.exitValue() == 0) {
    System.out.println("created the directory");
}

경로 이름의 공백

위의 예를 일반화하여 임의의 디렉토리를 만들 수 있다고 가정합니다.

Process p = Runtime.exec("mkdir " + dirPath);
// ...

이것은 일반적으로 작동하지만 dirPath 가 "/ home / user / My Documents"와 같은 경우 실패합니다. 문제는 exec(String) 가 공백을 찾기 만하면 문자열을 명령과 인수로 나눕니다. 명령 문자열 :

"mkdir /home/user/My Documents"

다음과 같이 분할됩니다.

"mkdir", "/home/user/My", "Documents"

이것은 "mkdir"명령이 두 개가 아닌 하나의 인수를 기대하기 때문에 실패하게됩니다.

이 때문에 일부 프로그래머는 경로 이름 주위에 따옴표를 추가하려고합니다. 이것도 작동하지 않습니다 :

"mkdir \"/home/user/My Documents\""

다음과 같이 분할됩니다.

"mkdir", "\"/home/user/My", "Documents\""

공백을 "인용"하기 위해 추가 된 여분의 큰 따옴표 문자는 공백이 아닌 다른 문자처럼 취급됩니다. 실제로, 우리가 인용하거나 공간을 벗어나는 일은 실패 할 것입니다.

이 특정 문제를 처리하는 방법은 exec(String ...) 오버로드를 사용하는 것입니다.

Process p = Runtime.exec("mkdir", dirPath);
// ...

dirpath 공백 문자가 포함 된 dirpath 작업은 exec 오버로드로 인해 인수를 분할하지 않으므로 작동합니다. 문자열은있는 그대로 OS exec 시스템 호출로 전달됩니다.

리디렉션, 파이프 라인 및 기타 셸 구문

외부 명령의 입력 또는 출력을 리디렉션하거나 파이프 라인을 실행하려고한다고 가정합니다. 예 :

Process p = Runtime.exec("find / -name *.java -print 2>/dev/null");

또는

Process p = Runtime.exec("find source -name *.java | xargs grep package");

첫 번째 예제는 파일 시스템의 모든 Java 파일 이름을 나열하고 두 번째 예제는 "소스"트리의 Java 파일에 package2 를 인쇄합니다.

이들은 예상대로 작동하지 않을 것입니다. 첫 번째 경우 "find"명령은 "2> / dev / null"을 명령 인수로 사용하여 실행됩니다. 리디렉션으로 해석되지 않습니다. 두 번째 예에서는 파이프 문자 ( "|")와 그 뒤에 오는 작업이 "찾기"명령에 제공됩니다.

여기서 문제는 exec 메소드와 ProcessBuilder 가 쉘 구문을 이해하지 못한다는 것입니다. 여기에는 리디렉션, 파이프 라인, 변수 확장, globbing 등이 포함됩니다.

간단한 경우 (예 : 단순 리디렉션) ProcessBuilder 사용하여 원하는 효과를 쉽게 얻을 수 있습니다. 그러나 이것은 일반적으로 사실이 아닙니다. 다른 방법은 셸에서 명령 줄을 실행하는 것입니다. 예 :

Process p = Runtime.exec("bash", "-c", 
                         "find / -name *.java -print 2>/dev/null");

또는

Process p = Runtime.exec("bash", "-c", 
                         "find source -name \\*.java | xargs grep package");

그러나 두 번째 예에서 와일드 카드 문자 ( "*")를 이스케이프 처리해야한다는 점에 유의해야합니다. 와일드 카드가 셸이 아닌 "찾기"로 해석되기를 원하기 때문입니다.

쉘 내장 명령이 작동하지 않습니다.

다음 예제가 UNIX 계열 쉘이있는 시스템에서 작동하지 않는다고 가정 해보십시오.

Process p = Runtime.exec("cd", "/tmp");     // Change java app's home directory

또는

Process p = Runtime.exec("export", "NAME=value");  // Export NAME to the java app's environment

이것이 작동하지 않는 몇 가지 이유가 있습니다.

  1. "cd"및 "export"명령은 쉘 내장 명령입니다. 이들은 별개의 실행 파일로 존재하지 않습니다.

  2. 쉘 내부 사용자가해야 할 일 (예 : 작업 디렉토리 변경, 환경 업데이트)을 수행하려면 해당 주가 상주하는 장소를 변경해야합니다. 정상적인 응용 프로그램 (Java 응용 프로그램 포함)의 경우 상태는 응용 프로그램 프로세스와 연관됩니다. 예를 들어, "cd"명령을 실행할 자식 프로세스는 부모 "java"프로세스의 작업 디렉토리를 변경할 수 없습니다. 비슷하게, 하나의 exec 프로세스는 그 뒤에 오는 프로세스의 작업 디렉토리를 변경할 수 없습니다.

이 추론은 모든 쉘 내장 명령에 적용됩니다.


1 - ProcessBuilder 도 사용할 수 있지만이 예제와 관련이 없습니다.

2 - 다소 거칠고 준비가되어 있습니다. 그러나이 방법의 실패는 예제와 관련이 없습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow