標籤:
java程式中要執行linux命令主要依賴2個類:Process和Runtime
首先看一下Process類:
ProcessBuilder.start() 和 Runtime.exec 方法建立一個本機進程,並返回 Process 子類的一個執行個體,該執行個體可用來控制進程並獲得相關資訊。
Process 類提供了執行從進程輸入、執行輸出到進程、等待進程完成、 檢查進程的退出狀態以及銷毀(殺掉)進程的方法。 建立進程的方法可能無法針對某些本機平台上的特定進程很好地工作,比如,本機視窗進程,守護進程,Microsoft Windows 上的 Win16/DOS 進程,或者 shell 指令碼。建立的子進程沒有自己的終端或控制台。它的所有標準 io(即 stdin、stdout 和 stderr) 操作都將通過三個流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重新導向到父進程。 父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。因為有些本機平台僅針對標準輸入和輸出資料流提供有限的緩衝區大小,如果讀寫子進程的輸出資料流或輸入資料流迅速出現失敗,則可能導致子進程阻塞,甚至產生死結。 當沒有 Process 對象的更多引用時,不是刪掉子進程,而是繼續非同步執行子進程。 對於帶有 Process 對象的 Java 進程,沒有必要非同步或並發執行由 Process 對象表示的進程。
特別需要注意的是:
1,建立的子進程沒有自己的終端控制台,所有標註操作都會通過三個流
(getOutputStream()、getInputStream() 和 getErrorStream()) 重新導向到父進程(父進程可通過這些流判斷子進程的執行情況)
2,因為有些本機平台僅針對標準輸入和輸出資料流提供有限的緩衝區大小,如果讀寫子進程的輸出資料流或輸入資料流迅速出現失敗,
則可能導致子進程阻塞,甚至產生死結
abstract void destroy() 殺掉子進程。 abstract int exitValue() 返回子進程的傳回值。根據慣例,值0表示正常終止。 abstract InputStream getErrorStream() 擷取子進程的錯誤流。 abstract InputStream getInputStream() 擷取子進程的輸入資料流。 abstract OutputStream getOutputStream() 擷取子進程的輸出資料流。 abstract int waitFor() 導致當前線程等待,如有必要,一直要等到由該 Process 對象表示的進程已經終止。 如果已終止該子進程,此方法立即返回。如果沒有終止該子進程,調用的線程將被阻塞,直到退出子進程。
特別需要注意:如果子進程中的輸入資料流,輸出資料流或錯誤流中的內容比較多,最好使用緩衝(注意上面的情況2)。
再來看一下Runtime類:
每個Java應用程式都有一個Runtime類執行個體,使應用程式能夠與其啟動並執行環境相串連。可以通過getRuntime方法擷取當前運行時環境。 應用程式不能建立自己的Runtime類執行個體。
介紹幾個主要方法:
Process exec(String command) 在單獨的進程中執行指定的字串命令。 Process exec(String command, String[] envp) 在指定環境的單獨進程中執行指定的字串命令。 Process exec(String command, String[] envp, File dir) 在有指定環境和工作目錄的獨立進程中執行指定的字串命令。 Process exec(String[] cmdarray) 在單獨的進程中執行指定命令和變數。 Process exec(String[] cmdarray, String[] envp) 在指定環境的獨立進程中執行指定命令和變數。 Process exec(String[] cmdarray, String[] envp, File dir) 在指定環境和工作目錄的獨立進程中執行指定的命令和變數。
command:一條指定的系統命令。
envp:環境變數字串數組,其中每個環境變數的設定格式為name=value;如果子進程應該繼承當前進程的環境,則該參數為null。
dir:子進程的工作目錄;如果子進程應該繼承當前進程的工作目錄,則該參數為null。
cmdarray:包含所調用命令及其參數的數組。
以下為樣本(要打成可執行jar包扔到linux下執行):
public class test { public static void main(String[] args){ InputStream in = null; try { Process pro = Runtime.getRuntime().exec(new String[]{"sh", "/home/test/test.sh","select admin from M_ADMIN", "/home/test/result.txt"}); pro.waitFor(); in = pro.getInputStream(); BufferedReader read = new BufferedReader(new InputStreamReader(in)); String result = read.readLine(); System.out.println("INFO:"+result); } catch (Exception e) { e.printStackTrace(); } } }
在這用的是Process exec(String[] cmdarray)這個方法
/home/test/test.sh指令碼如下:
#!/bin/sh #查詢sql SQL=$1 #查詢結果儲存檔案 RESULT_FILE=$2 #資料庫連接 DB_NAME=scott DB_PWD=tiger DB_SERVER=DB_TEST RESULT=`sqlplus -S ${DB_NAME}/${DB_PWD}@${DB_SERVER}<< ! set heading off set echo off set pages 0 set feed off set linesize 3000 ${SQL} / commit / !` echo "${RESULT}" >> ${RESULT_FILE} echo 0;
特別需要注意的是,當需要執行的linux命令帶有管道符時(例如:ps -ef|grep java),用上面的方法是不行的,解決方式是將需要執行的命令作為參數傳給shell
public class Test { public static void main(String[] args) throws Exception{ String[] cmds = {"/bin/sh","-c","ps -ef|grep java"}; Process pro = Runtime.getRuntime().exec(cmds); pro.waitFor(); InputStream in = pro.getInputStream(); BufferedReader read = new BufferedReader(new InputStreamReader(in)); String line = null; while((line = read.readLine())!=null){ System.out.println(line); } } }
PS:
Runtime.getRuntime().exec()這種調用方式在java虛擬機器中是十分消耗資源的,即使命令可以很快的執行完畢,頻繁的調用時建立進程消耗十分客觀。
java虛擬機器執行這個命令的過程是,首先複製一條和當前虛擬機器擁有一樣環境變數的進程,再用這個新的進程執行外部命令,最後退出這個進程。頻繁的建立對CPU和記憶體的消耗很大。
From:
http://blog.csdn.net/a19881029/article/details/8063758
http://www.avajava.com/tutorials/lessons/how-do-i-run-another-application-from-java.html
http://stackoverflow.com/questions/13991007/execute-external-program-in-java
http://stackoverflow.com/questions/15218892/running-a-java-program-from-another-java-program
http://www.rgagnon.com/javadetails/java-0014.html
http://www.javacoffeebreak.com/faq/faq0030.html
http://www.youtube.com/watch?v=AiVpzFH9zRc
Java程式執行Linux命令(JSP運行其他程式)