標籤:rac 擷取 quartz 通知 常見 user font 檔案名稱 stack
最近由於工作需要把使用者配置的Hive命令在Linux環境下執行,專門做了一個使用者管理介面特地研究了這個不經常用得ProcessBuilder類。所以把自己的學習的資料總結一下。
一、概述
ProcessBuilder類是J2SE 1.5在java.lang中新添加的一個新類,此類用於建立作業系統進程,它提供一種啟動和管理進程(也就是應用程式)的方法。在J2SE 1.5之前,都是由Process類處來實現進程的控制管理。
每個 ProcessBuilder 執行個體管理一個進程屬性集。它的start() 方法利用這些屬性建立一個新的 Process 執行個體。start() 方法可以從同一執行個體重複調用,以利用相同的或相關的屬性建立新的子進程。 (我在《深入研究java.lang.Runtime類》中講過,進程也可以由Runtime.exec()啟動。)
每個進程產生器(即ProcessBuilder對象)管理這些進程屬性:
命令 是一個字串列表,它表示要調用的外部程式檔案及其參數(如果有)。在此,表示有效作業系統命令的字串列表是依賴於系統的。例如,每一個總體變數,通常都要成為此列表中的元素,但有一些作業系統,希望程式能自已標幟命令列字串——在這種系 統中,Java 實現可能需要命令確切地包含這兩個元素。
環境 是從變數 到值 的依賴於系統的映射。初始值是當前進程環境的一個副本(請參閱 System.getenv())。
工作目錄 預設值是當前進程的當前工作目錄,通常根據系統屬性 user.dir 來命名。
redirectErrorStream 屬性。最初,此屬性為 false,意思是子進程的標準輸出和錯誤輸出被發送給兩個獨立的流,這些流可以通過 Process.getInputStream() 和 Process.getErrorStream() 方法來訪問。如果將值設定為 true,標準錯誤將與標準輸出合并。這使得關聯錯誤訊息和相應的輸出變得更容易。在此情況下,合并的資料可從 Process.getInputStream() 返回的流讀取,而從 Process.getErrorStream() 返回的流讀取將直接到達檔案尾。
既然有Process類,那為什麼還要發明個ProcessBuilder類呢?ProcessBuilder和Process兩個類有什麼區別呢?
ProcessBuilder為進程提供了更多的控制,例如,可以設定當前工作目錄,還可以改變環境參數。而Process的功能相對來說簡單的多。ProcessBuilder是一個final類,有兩個帶參數的構造方法,你可以通過構造方法來直接建立ProcessBuilder的對象。而Process是一個抽象類別,一般都通過Runtime.exec()和ProcessBuilder.start()來間接建立其執行個體。
注意:
修改進程構建器的屬性將影響後續由該對象的 start() 方法啟動的進程,但從不會影響以前啟動的進程或 Java 自身的進程。
ProcessBuilder類不是同步的。如果多個線程同時訪問一個 ProcessBuilder,而其中至少一個線程從結構上修改了其中一個屬性,它必須保持外部同步。 啟動一個使用預設工作目錄和環境的新進程: Process p = new ProcessBuilder("myCommand", "myArg").start(); 下面是一個利用修改過的工作目錄和環境啟動進程的例子: ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
Map<String, String> env = pb.environment();
env.put("VAR1", "myValue");
env.remove("OTHERVAR");
env.put("VAR2", env.get("VAR1") + "suffix");
pb.directory("myDir");
Process p = pb.start();
要利用一組明確的環境變數啟動進程,在添加環境變數之前,首先調用 Map.clear()。
二、API預覽
構造方法摘要
ProcessBuilder(List<String> command)
利用指定的作業系統程式和參數構造一個進程產生器。
ProcessBuilder(String... command)
利用指定的作業系統程式和參數構造一個進程產生器。
方法摘要
command()
返回此進程產生器的作業系統程式和參數。
command(List<String> command)
設定此進程產生器的作業系統程式和參數。
command(String... command)
設定此進程產生器的作業系統程式和參數。
directory()
返回此進程產生器的工作目錄。
directory(File directory)
設定此進程產生器的工作目錄。
environment()
返回此進程產生器環境的字串映射視圖。
redirectErrorStream()
通知進程產生器是否合并標準錯誤和標準輸出。
redirectErrorStream(boolean redirectErrorStream)
設定此進程產生器的 redirectErrorStream 屬性。
start() 使用此進程產生器的屬性啟動一個新進程執行個體Process ,可以操作
Process對象:詳解
三、常見應用
若要使用ProcessBuilder建立一個進程,只需要建立ProcessBuilder的一個執行個體,指定該進程的名稱和所需參數。要執行此程式,調用該執行個體上的start()即可。1,下面上一個執行Windows記事本的例子。注意它將要編輯的檔案名稱指定為一個參數。
class PBDemo { public static void main(String args[]) { try { ProcessBuilder proc = new ProcessBuilder("notepad.exe", "testfile"); proc.start(); } catch (Exception e) { System.out.println("Error executing notepad."); } } }
2,ProcessBuilder的應用簡單來說就是,先產生一個ProcessBuilder對象:
ProcessBuilder提供了兩個建構函式,ProcessBuilder(List<String> command) 和 ProcessBuilder(String... command
我這裡使用的是第二個,因為是需要在Linux系統上執行shell指令碼,所以,我的建構函式為:
ProcessBuilder pb = new ProcessBuilder("sh"); (ps:你也可以對應的使用“Python”或者在windows上使用“cmd”)
執行的命令的應用程式已經選好了,那我們的輸入和輸出呢,怎麼設定,這是我們關心的地方。
推薦使用redirectInput(File file) ,讓我們用這個方法來擷取輸入,同理使用redirectOutput(File file)
來處理輸出,如果有錯誤的話,還需要使用redirectError(File file)
由於涉及到的參數都是File類型的,所以,你需要先產生這幾個檔案,然後再使用它們
執行完後,可以去outfile裡面查看執行結果,你也可以把結果讀出後返回,將file.sh和outfile這兩個臨時檔案刪除。
package com.pmqin.quartz.domian;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.text.SimpleDateFormat;import java.util.Date;public class ProcessBuildertest { public static void main(String[] args) { String file = "!/bin/sh\r\n" // linux 的換行是\n,注意換 + "a=\‘Hello world\‘\r\n" + "echo $a\r\n"; String result = Process("sh", file,true); System.out.println(result); } catch (Exception e) { System.out.println("Error executing notepad."); } }public static String Process(String cmd, String commandlist,boolean isSavefile) throws IOException, InterruptedException { File input = new File("./file.sh"); createNewFile(input); PrintWriter print = new PrintWriter(input); print.append(commandlist); print.flush(); print.close(); String result = "SUCCESS"; try { File output = null; if (isSavefile) { output = new File("./outfile"); createNewFile(output); } ProcessBuilder pb = new ProcessBuilder(cmd); pb.redirectErrorStream(true);//如果將值設定為 true,標準錯誤將與標準輸出合并,在此情況下,合并的資料可從 Process.getInputStream() 返回的流讀取 pb.redirectInput(input); if (isSavefile) { pb.redirectOutput(output); //表示把輸出重新導向到指定的檔案裡面 } Process process = pb.start();//非同步 // 擷取執行的結果 if (!isSavefile) { InputStream in = process.getInputStream();//同步 System.out.println("等待執行完成"); String streamtxt=getInputStream(in); System.out.println(streamtxt); } if (process.waitFor() != 0) { //InputStream error = process.getErrorStream(); result="Err"; } process.destroy(); } catch (IOException e) { result = "FAIL"; e.printStackTrace(); } return result; }/** * inputStream 檔案流 轉成字串 * @param inputStream 檔案流 * @return * @throws IOException */ public static String getInputStream(InputStream inputStream) throws IOException { StringBuilder stringBuilder = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "gb2312")); String line = null; while ((line = bufferedReader.readLine()) != null) { stringBuilder.append(System.getProperty("line.separator")); stringBuilder.append(line); } return stringBuilder.toString(); } /** * 判斷檔案是否存在,不存在就建立 * @param file * @throws IOException */ public static void createNewFile(File file) throws IOException { if (!file.exists()) { file.createNewFile(); } }
淺析Java.lang.ProcessBuilder類