본문 바로가기

Java

자바에서 유닉스 명령어 실행

자바 어플리케이션으로부터 프로그램 호출

 

Tech Tips 1999년 12월 14일 판(http://developer.java.sun.com/developer/TechTips/1999/tt1214.html)에서 RMI (Remote Method Invocation)가 프로그램간의 통신에 어떻게 사용될 수 있는지 보십시오. 또 다른 통신 기술은 Runtime.exec 메소드입니다. 실행중인 자바 응용프로그램으로부터 프로그램을 불러내기 위해 이 메소드를 이용할 수 있습니다. Runtime.exec는 또한 프로그램의 표준 입출력 조정, 실행을 완료할 때까지 대기, 종료 상태의 획득 등의 프로그램에 관련된 연산을 수행할 수 있도록 합니다. 여기 이러한 특징을 보여주는 간단한 C 응용프로그램이 있습니다.

    #include 
    int main()
    { 
        printf("testing\n"); 
        return 0; 
    }

이 응용프로그램은 표준 출력에 "testing"이라는 문자열을 쓰고, 종료상태를 0으로 하여 프로그램을 마칩니다.

이 단순한 프로그램을 자바 응용프로그램에서 수행하고자 한다면, C 응용프로그램을 아래와 같이 컴파일한 후

    $ cc test.c -o test

(C 컴파일러에 따라 다른 매개변수를 필요로 할지도 모릅니다) 그리고 아래와 같은 자바 코드를 이용하여 프로그램을 불러냅니다.

    import java.io.*; 
    import java.util.ArrayList; 
 
    public class ExecDemo
    { 
        static public String[] runCommand(String cmd) 
            throws IOException
        { 
            // 명령행 출력 라인 캡쳐를 위한 list 설정
            ArrayList list = new ArrayList(); 
 
            // 명령 실행 
            Process proc = Runtime.getRuntime().exec(cmd); 
 
            // 명령행의 출력 스트림을 얻고
            // 그 내용을 buffered reader input stream에 입력한다.
            InputStream istr = proc.getInputStream(); 
            BufferedReader br = new BufferedReader(new InputStreamReader(istr)); 
 
            // 명령행에서의 출력 라인 읽음
            String str; 
            while ((str = br.readLine()) != null) 
                list.add(str); 
 
            // 명령행이 종결되길 기다린다
            try 
            { 
                proc.waitFor(); 
            } 
            catch (InterruptedException e) 
            {
                System.err.println("process was interrupted"); 
            } 
 
            // 종료값을 체크
            if (proc.exitValue() != 0) 
            System.err.println("exit value was non-zero"); 
 
            // stream을 닫는다
            br.close(); 
 
            // caller에게 문자열 리스트를 돌려준다
            return (String[])list.toArray(new String[0]); 
        } 
 
        public static void main(String args[]) throws IOException 
        { 
            try
            { 
                // command를 실행시킨다
                String outlist[] = runCommand("test"); 
 
                // 그것의 출력을 디스플레이한다
                for (int i = 0; i < outlist.length; i++) 
                    System.out.println(outlist[i]); 
            } 
            catch (IOException e)
            {
                System.err.println(e); 
            } 
        } 
    } 

데모는 실제로 프로그램을 실행시키기 위해 runCommand 라는 메소드를 호출합니다.

    String outlist[] = runCommand("test");

이 메소드는 프로그램의 출력을 읽고 문자열의 리스트로 저장할 수 있도록 프로그램의 출력 스트림을 입력 스트림에 저장합니다.

    InputStream istr = proc.getInputStream(); 
    BufferedReader br = new BufferedReader(new InputStreamReader(istr)); 
 
    String str; 
    while ((str = br.readLine()) != null) 
        list.add(str); 

출력이 모두 읽힌 후에, 프로그램이 종료되는 것을 기다리는 waitFor 를 불러낸 후, 프로그램의 종료값을 얻기 위해 exitValue를 호출합니다. 유닉스 시스템 호출과 같은 시스템 프로그래밍 경험이 있다면, 이러한 접근방법이 익숙할 것입니다. (이 예에서는 현재 디렉토리가 쉘 탐색 경로에 있음을 가정합니다. 이 주제에 관해서는 아래에서 더욱 자세히 다룹니다.)

현 디렉토리의 전체 파일 목록을 얻기 위해서 UNIX 시스템 사용자들은

    runCommand("test");

    runCommand("ls -l"); 

로 바꿀 수 있습니다.

하지만 목록을 이러한 방법으로 얻는 것은 Runtime.exec 의 가장 근본적인 약점을 드러냅니다--불러내는 각 프로그램들이 모두 이식성을 가지고 있지는 않을 것입니다. 즉, Runtime.exec 은 이식할 수 있으며, 다양한 자바 구현에 관계없이 존재하지만, 그것으로 불러낸 프로그램들은 반드시 그렇지 않을 수 있다는 것입니다. 윈도우즈 시스템에서 "ls" 란 프로그램은 없습니다.

Windows NT를 실행하고 있으며 이 문제를 해결하기 위해 다음과 같이

    runCommand("dir"); 

"ls"와 같은 명령인 "dir"로 수정한다고 가정합니다. 이것은 동작하지 않습니다. 왜냐하면 "dir" 는 수행 가능한 프로그램이 아니기 때문입니다. 그 대신에 쉘(명령 번역기) CMD.EXE에 내장되어 있습니다. 즉,

    runCommand("cmd /c dir"); 

이라고 명령을 내려야 합니다. 여기에서 "cmd /c command" 는 "쉘을 불러내어서 특정한 명령을 수행하고 종료하여라" 라는 뜻입니다. 이와 유사하게, Korn 쉘과 같은 UNIX 쉘의 사용자는

    runCommand("ksh -c alias"); 

라고 명령하면 됩니다, 여기에서 "alias"는 쉘 내부에 내장된 명령입니다. 이 예제에서의 출력물을 모든 쉘 alias 목록일 것입니다.

위의 디렉토리 리스트를 얻는 예제를, 이식성 있는 자바의 특성을 이용하여 같은 결과를 얻도록 할 수 있습니다. 예를 들어

    import java.io.File; 

    public class DumpFiles 
    { 
        public static void main(String args[]) 
        { 
            String list[] = new File(".").list(); 
            for (int i = 0; i < list.length; i++) 
                System.out.println(list[i]); 
        } 
    } 

라고 하면 현 디렉토리의 모든 파일과 디렉토리의 리스트를 얻을 수 있습니다. 즉, ls/dir 를 쓰는 것은 거의 모든 상황에서 상식에 맞는 방법이 아닌 것입니다.

Runtime.exec 를 쓰는 것이 적합한 상황은 사용자로 하여금 파일을 편집하기 위해서 에디터나 워드 프로세서( 이멕스, Vi 또는 워드 등 ) 를 규정할 수 있게 하는 경우입니다. 이것은 대형 애플리케이션에 있어서는 일반적인 특징입니다. 이러한 애플리케이션은 에디터로의 로컬 경로를 가지고 있는 설정파일이 있을 것이며, Runtime.exec 는 이 경로를 이용해 호출하게 됩니다.

Runtime.exec에서 신중을 요하는 점들 중 하나는 파일을 찾는 것입니다. 예를 들어

    Runtime.exec("ls"); 

라고 한다면 "ls" 프로그램을 어떻게 찾을 수 있겠습니까? JDK 1.2.2를 이용한 실험들은 PATH 환경변수가 탐색된다는 것을 보여줍니다. 이것은 쉘에서 명령을 실행할 때와 같습니다. 하지만 문서는 이 점에 주목하지 않으므로 조심해야 합니다. 탐색 경로가 설정되어 있다고 가정해서는 안됩니다. 절대경로가 명시되어 있다면 위에서 제시한 것처럼 제한된 방법으로 Runtime.exec를 실행하는 것이 더 상식적인 방법일 수 있습니다.

이 외에도 환경 문자열을 특성화할 수 있도록 해 주는 여러가지 방법의 Runtime.exec 사용법이 있습니다.
 

[출처 : http://www.javastudy.co.kr/docs/techtips/000215.html ]

--------------------------------------------------------------------------------------

다음과 같은 메소드도 있다.

Process exec(String[] cmdarray)
Executes the specified command and arguments in a separate process.

String[] comm = {"/bin/sh", "-c", "ps -ex | grep XXX" };
Process ps = Runtime.getRuntime().exec(comm );

--------------------------------------------------------------------------------------

자바서비스넷을 검색하다 보니..

그러나 windows환경의 command.com의 경우, "command.com /c dir" 등의 방법으로
될 것으로 예상됐지만, "command.com /c dir" 의 명령이 보통의 DOS창에선 바로 exit으로
이어지나, Runtime.exec("command.com /c dir") 로 호출했을 경우, Process Thread가
끝나질 않는 군요... 결국, p.waitFor()에서 blocking 현상이 일어나네요.

다음 글도 참고하라더군.

http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=1

Why Runtime.exec() hangs
The JDK's Javadoc documentation provides the answer to this question:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.

출처 : http://blog.naver.com/PostView.nhn?blogId=bigsy01&logNo=50033762184&redirect=Dlog&widgetTypeCall=true

 

'Java' 카테고리의 다른 글

생성자(Constructor)도 메소드인가?  (0) 2016.07.26
Java 연산자  (0) 2012.11.28
Java Static  (0) 2012.11.28
Java Swing의 Parser을 이용하여 HTML 데이터를 읽어들여 Parsing 을 하기  (0) 2012.03.23
Java Swing HTML Parser#  (0) 2012.03.23