source

파이프를 Runtime.exec()과 함께 작동시키는 방법

lovecheck 2022. 11. 8. 21:13
반응형

파이프를 Runtime.exec()과 함께 작동시키는 방법

다음 코드를 고려합니다.

String commandf = "ls /etc | grep release";

try {

    // Execute the command and wait for it to complete
    Process child = Runtime.getRuntime().exec(commandf);
    child.waitFor();

    // Print the first 16 bytes of its output
    InputStream i = child.getInputStream();
    byte[] b = new byte[16];
    i.read(b, 0, b.length); 
    System.out.println(new String(b));

} catch (IOException e) {
    e.printStackTrace();
    System.exit(-1);
}

프로그램의 출력은 다음과 같습니다.

/etc:
adduser.co

물론 셸에서 실행하면 예상대로 작동합니다.

poundifdef@parker:~/rabbit_test$ ls /etc | grep release
lsb-release

인터넷에서는 파이프 동작이 크로스 플랫폼이 아니기 때문에 자바 공장에서 Java를 생산하는 똑똑한 사람들은 파이프가 작동한다고 보장할 수 없다고 합니다.

이거 어떻게 해?

모든 파싱은 Java Constructs를 사용하여 실행하지 않습니다.grep그리고.sed언어를 바꾸려면 해당 언어로 구문 분석 코드를 다시 작성해야 합니다.이것은 완전히 금지되어 있습니다.

셸 명령어를 호출할 때 Java가 파이프 및 리다이렉션을 수행하도록 하려면 어떻게 해야 합니까?

스크립트를 작성하고 개별 명령 대신 스크립트를 실행합니다.

파이프는 셸의 일부이므로 다음과 같은 작업을 수행할 수도 있습니다.

String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};

Process p = Runtime.getRuntime().exec(cmd);

Linux에서도 비슷한 문제가 발생했지만 "ps -ef | grep some process"라는 점이 다릅니다.
적어도 "ls"를 사용하면 언어에 의존하지 않는(느리지만) Java를 대체할 수 있습니다.예:

File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
    if (file.matches(.*some.*)) { System.out.println(file); }
}

"ps"의 경우 Java에는 API가 없는 것 같아서 조금 어렵습니다.

Sigar가 도움을 줄 수 있다고 들었습니다.https://support.hyperic.com/display/SIGAR/Home

그러나 (Kaj가 지적한 바와 같이) 가장 간단한 해결책은 piped 명령을 문자열 배열로 실행하는 것입니다.다음은 전체 코드입니다.

try {
    String line;
    String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
    Process p = Runtime.getRuntime().exec(cmd);
    BufferedReader in =
            new BufferedReader(new InputStreamReader(p.getInputStream()));
    while ((line = in.readLine()) != null) {
        System.out.println(line); 
    }
    in.close();
} catch (Exception ex) {
    ex.printStackTrace();
}

String 배열이 파이프와 함께 작동하는 이유에 대해서는 단일 문자열은 작동하지 않습니다.우주의 미스터리 중 하나입니다(특히 소스코드를 읽지 않은 경우).이그제큐티브가1개의 스트링을 부여받으면 (우리가 싫어하는 방식으로) 먼저 해석하기 때문이라고 생각합니다.반대로 exec이 문자열 배열을 지정받으면 해석하지 않고 단순히 운영체제로 전달됩니다.

실제로 바쁜 하루 중 시간을 내어 소스 코드(http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29)를 살펴보면 다음과 같은 현상이 발생하고 있음을 알 수 있습니다.

public Process  [More ...] 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);
}

각 프로세스를 실행할 Runtime을 만듭니다.첫 번째 런타임에서 OutputStream을 가져와 두 번째 런타임에서 InputStream으로 복사합니다.

@Kaj가 받아들인 답변은 Linux용입니다.Windows 의 경우는, 다음과 같습니다.

String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);

언급URL : https://stackoverflow.com/questions/5928225/how-to-make-pipes-work-with-runtime-exec

반응형