Java程式執行Linux命令
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下執行):
- publicclass test {
- publicstaticvoid 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
- publicclass Test {
- publicstaticvoid 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和記憶體的消耗很大