poniedziałek, 16 maja 2011

Java: wywołanie komendy systemowej

Aktualnie pracuję nad projektem, który potrzebuję zintegrować z kompilatorem i debuggerem języka ANSI C. Oczywiście projekt ten powstaje w języku Java co jest dla mnie niezwykle typowe. Oznacza to, że potrzebuję z poziomu aplikacji napisanej w języku Java uruchomić kompilator ANSI C dla odpowiedniego pliku, a następnie dla uzyskanego pliku uruchomić debugger i nim sterować. Najważniejsze w tym wypadku to możliwość uruchomienia zewnętrznej aplikacji z poziomu programu napisanego w Javie.

W zasadzie powyższe zadanie nie jest trudne, a poszukiwanie rozwiązania nie zajęło za wiele czasu. W celu zobrazowania metody uruchamiania komend systemowych z poziomu aplikacji w Javie posłużę się przykładem z listowaniem zawartości folderu. Scenariusz jest praktycznie taki sam dla każdego polecenia. Aby wykonać postawione zadanie należy posłużyć się klasą Process i metodą exec(String command) klasy Runtime.

Process p = Runtime.getRuntime().exec("ps -ef");
Powyższa linia w zasadzie rozwiązuje problem wywołania komendy. Metoda exec(String command)  odpowiada funkcji exec znanej z systemów *nix. W formie podanej powyżej przyda się jednak tylko wtedy gdy nie mamy interesuje nas ani wejście, ani wyjście tego procesu, czyli dla wybranej jako przykładowa komendy jest to jeszcze niezbyt użyteczne wykorzystanie. Następnym etapem jest więc podłączenie do standardowego wyjścia i wyjścia błędów tej komendy.

BufferedReader stdInput = new BufferedReader(new
    InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new
    InputStreamReader(p.getErrorStream()));

System.out.println("Wyjście polecenia:\n");
while ((s = stdInput.readLine()) != null)
    System.out.println(s);
System.out.println("Wyjście błędów:\n");
while((s = stdError.readLine()) != null)
    System.out.println(s);

Jak łatwo zauważyć z procesu p pobierane są wyjścia standardowe i błędów, a  na ich podstawie tworzone są obiekty klasy BufferedReader. Dalej to już zwykłe czytanie po linii z wyjścia aż do jego zakończenia. Tutaj mała uwaga. Jeśli nie pojawi się koniec wyjścia, nasz program też się nie zakończy.

Klasa Proccess posiada jeszcze metodę getOutputStream(), króra pozwala na pobranie strumienia wejściowego do procesu. Służy on do pisania do procesu. Ja osobiście wykorzystam go do sterowania gdb. W opisanym przykładzie nie był on akurat potrzebny, ale jak łatwo się domyślić jego użycie nie różni się znacząco od wykorzystania opisanych strumieni. Co dość ważne nasza aplikacja nie będzie czekać na zakończenie subprocesu jeśli nie zostanie do tego "zmuszona". Aby aktualny wątek czekał na zakończenie uruchomionego procesu p należy skorzystać z poniższej metody.

p.waitFor();
Największą zaletą języka Java jest przenaszalność programów. Zgodnie z tym jar przygotowany na systemie Windows będzie działał tak samo na systemie FreeBSD. Niestety użycie komendy systemowej może pozbawić tej cechy. W przypadku użycia komend systemów z rodziny Linux/Unix rozwiązaniem jest skorzystanie z projektu Cygwin na systemie Windows.

Brak komentarzy:

Prześlij komentarz