馴服Tiger之訪問環境變量和調用子進程
訪問平臺專有的信息并不是一件容易的事。雖然可以使用 Runtime.exec() 創建進程,但由于平臺之間的差異,構造參數集常常令人頭痛不已。此外, System 的 getenv() 方法從開始進行 Java 編程就遭到反對。
什么時候一個遭到反對的方法不再被反對呢?使用 System 的 getenv() 方法時就會遇到這種情況。Tiger 以 1995 年正式發布之前的 Java 平臺為基礎,它沒有拋棄該方法,同時還提供了一個新的類 ProcessBuilder (屬于包 java.lang ),用它來創建進程并與系統進程交互。
訪問環境變量
雖然我個人并不想退回到原來使用 AWT 組件的事件模型,但是早期的 Java 平臺版本(稱為 alpha 版)有一個很好的特性,即能夠訪問環境變量。該方法和當時“編寫一次,隨處運行”的頌詞背道而馳,因此當 Java 平臺發布 1.0 版時, System 的 getenv() 方法受到了抨擊。雖然 1.0 版為何拋棄原來的一些內容一直令我迷惑不解,但我常常看到該方法引起新入門開發人員的興趣。時間回到 2004 年,您現在終于能夠使用這個方法了。如清單 1 所示,該方法的使用很簡單:
清單 1. 調用 getenv
只要在命令行中隨 getenv 調用傳入變量的名稱,就可以得到它的當前值。比如在我那臺用了兩年的桌面機上,如果輸入參數 PROCESSOR_IDENTIFIER ,就會得到清單 2 所示的結果:
清單 2. getenv 的輸出結果
首先要注意的是方法名 getenv() ,它完全采用小寫形式,而不是采用您所預料的大小寫混合形式( getEnv() )。這是因為在正式發布之前的最初命名方法就是這樣的。其次,訪問環境變量常常要使用平臺專用的代碼。如果確實希望這樣做也可以,但這樣就偏離了百分之百的純 Java 模型。上述代碼本身仍然是純粹的 Java 代碼,因此使用該方法并不完全違背這一原則,但是使用了這么多年的系統屬性之后,使用 getenv() 感覺怪怪的。
Tiger 提供了兩個版本的 getenv() 方法,而不是一個。第二個版本返回與系統中當前設置的所有環境變量對應的‘名/值’對(name-value pairs)。清單 3 說明了這種新方法的應用,并打印出了所有環境變量的鍵和值:
清單 3. 訪問所有的環境變量
理解 ProcessBuilder
這為我們帶來了一個新的類 java.lang.ProcessBuilder 。平臺的早期版本允許通過 Runtime 類的 exec() 方法創建本機進程。該方法仍然有效,但是因為能以 String 數組作為參數、以 File 參數作為工作目錄,所以用這種方法定制子進程比較困難。使用 ProcessBuilder 可以簡化這個過程,它提供了 directory(File) 方法來改變進程的工作目錄,可以用 environment() 方法在進程空間中添加和刪除環境變量。清單 4 說明了 ProcessBuilder 的一種簡單用法,它使用 ipconfig 命令獲得 Internet 配置信息。該方法適用于多數平臺,否則可以將 ipconfig 改寫成所用平臺上的工作命令。啟動進程構造程序之后,需要獲得其 InputStream ,以讀入所創建進程的結果。
清單 4. 使用 ProcessBuilder
如清單 5 所示,該程序的運行結果與在命令行中執行 ipconfig 所得到的結果類似(您得到的結果看起來可能有所不同):
清單 5. ProcessBuilder 的輸出結果
如前所述, ProcessBuilder 類不僅能生成新的進程,而且還能獲得其結果。在調用其 start() 方法之前,還可以調整進程所執行的上下文。如果不喜歡環境變量,您可以使用 environment 獲得當前設置,并調用 clear() 清除映射。如果需要添加環境變量,可以調用 environment 獲得當前設置,然后通過 put(name, value) 添加新的變量。如果希望使用新的工作目錄,可以調用 directory() 并提供新的工作目錄作為 File 對象。就是這么簡單。使用表示將運行的命令及其參數的數目可變的字符串參數來創建 ProcessBuilder ,一旦使用新的環境變量和工作目錄配置 ProcessBuilder ,就可以調用 start() 來執行命令。
結束語
您希望您所喜歡的方法在遭到反對之后再受到歡迎嗎?當然,有時候,一個受到抨擊的方法雖然從沒在 Java 發行版本中受到真正的支持,但它可能重新獲得新生。只要有足夠多的用戶在 Sun 的 Bug Parade 上呼吁和投票,開發人員就可以改變 Java 平臺的演進方向。雖然我曾懷疑過時的 AWT 事件模型會卷土重來——盡管每個人都這樣要求,但只是一些簡單的問題(如訪問環境變量)最終得到了 Java 平臺的支持。小心地使用它。除了反對 getenv 的問題之外, ProcessBuilder 還提供了一種創建本機進程的簡單方法,應該用它來代替所有過時的 Runtime.exec() 調用。開始重構吧!
閱讀關于 Java 的全部文章
什么時候一個遭到反對的方法不再被反對呢?使用 System 的 getenv() 方法時就會遇到這種情況。Tiger 以 1995 年正式發布之前的 Java 平臺為基礎,它沒有拋棄該方法,同時還提供了一個新的類 ProcessBuilder (屬于包 java.lang ),用它來創建進程并與系統進程交互。
訪問環境變量
雖然我個人并不想退回到原來使用 AWT 組件的事件模型,但是早期的 Java 平臺版本(稱為 alpha 版)有一個很好的特性,即能夠訪問環境變量。該方法和當時“編寫一次,隨處運行”的頌詞背道而馳,因此當 Java 平臺發布 1.0 版時, System 的 getenv() 方法受到了抨擊。雖然 1.0 版為何拋棄原來的一些內容一直令我迷惑不解,但我常常看到該方法引起新入門開發人員的興趣。時間回到 2004 年,您現在終于能夠使用這個方法了。如清單 1 所示,該方法的使用很簡單:
清單 1. 調用 getenv
| public class EnvTest { public static void main(String args[]) { System.out.println(System.getenv(args[0])); } } |
只要在命令行中隨 getenv 調用傳入變量的名稱,就可以得到它的當前值。比如在我那臺用了兩年的桌面機上,如果輸入參數 PROCESSOR_IDENTIFIER ,就會得到清單 2 所示的結果:
清單 2. getenv 的輸出結果
| java EnvTest PROCESSOR_IDENTIFIER x86 Family 6 Model 8 Stepping 6, GenuineIntel |
首先要注意的是方法名 getenv() ,它完全采用小寫形式,而不是采用您所預料的大小寫混合形式( getEnv() )。這是因為在正式發布之前的最初命名方法就是這樣的。其次,訪問環境變量常常要使用平臺專用的代碼。如果確實希望這樣做也可以,但這樣就偏離了百分之百的純 Java 模型。上述代碼本身仍然是純粹的 Java 代碼,因此使用該方法并不完全違背這一原則,但是使用了這么多年的系統屬性之后,使用 getenv() 感覺怪怪的。
Tiger 提供了兩個版本的 getenv() 方法,而不是一個。第二個版本返回與系統中當前設置的所有環境變量對應的‘名/值’對(name-value pairs)。清單 3 說明了這種新方法的應用,并打印出了所有環境變量的鍵和值:
清單 3. 訪問所有的環境變量
| import java.util.Map; public class EnvDump { public static void main(String args[]) { for (Map.Entry entry: System.getenv().entrySet()) { System.out.println(entry.getKey() + " / " + entry.getValue()); } } } |
理解 ProcessBuilder
這為我們帶來了一個新的類 java.lang.ProcessBuilder 。平臺的早期版本允許通過 Runtime 類的 exec() 方法創建本機進程。該方法仍然有效,但是因為能以 String 數組作為參數、以 File 參數作為工作目錄,所以用這種方法定制子進程比較困難。使用 ProcessBuilder 可以簡化這個過程,它提供了 directory(File) 方法來改變進程的工作目錄,可以用 environment() 方法在進程空間中添加和刪除環境變量。清單 4 說明了 ProcessBuilder 的一種簡單用法,它使用 ipconfig 命令獲得 Internet 配置信息。該方法適用于多數平臺,否則可以將 ipconfig 改寫成所用平臺上的工作命令。啟動進程構造程序之后,需要獲得其 InputStream ,以讀入所創建進程的結果。
清單 4. 使用 ProcessBuilder
| import java.io.*; public class ProcessTest { public static void main(String args[]) throws IOException { Process p = new ProcessBuilder("ipconfig").start(); InputStream is = p.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; while ((line = br.readLine()) != null) { System.out.println(line); } } } |
如清單 5 所示,該程序的運行結果與在命令行中執行 ipconfig 所得到的結果類似(您得到的結果看起來可能有所不同):
清單 5. ProcessBuilder 的輸出結果
| Windows 2000 IP Configuration Ethernet adapter Local Area Connection: Connection-specific DNS Suffix . : IP Address. . . . . . . . . . . . : 192.168.0.101 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 192.168.0.1 |
如前所述, ProcessBuilder 類不僅能生成新的進程,而且還能獲得其結果。在調用其 start() 方法之前,還可以調整進程所執行的上下文。如果不喜歡環境變量,您可以使用 environment 獲得當前設置,并調用 clear() 清除映射。如果需要添加環境變量,可以調用 environment 獲得當前設置,然后通過 put(name, value) 添加新的變量。如果希望使用新的工作目錄,可以調用 directory() 并提供新的工作目錄作為 File 對象。就是這么簡單。使用表示將運行的命令及其參數的數目可變的字符串參數來創建 ProcessBuilder ,一旦使用新的環境變量和工作目錄配置 ProcessBuilder ,就可以調用 start() 來執行命令。
結束語
您希望您所喜歡的方法在遭到反對之后再受到歡迎嗎?當然,有時候,一個受到抨擊的方法雖然從沒在 Java 發行版本中受到真正的支持,但它可能重新獲得新生。只要有足夠多的用戶在 Sun 的 Bug Parade 上呼吁和投票,開發人員就可以改變 Java 平臺的演進方向。雖然我曾懷疑過時的 AWT 事件模型會卷土重來——盡管每個人都這樣要求,但只是一些簡單的問題(如訪問環境變量)最終得到了 Java 平臺的支持。小心地使用它。除了反對 getenv 的問題之外, ProcessBuilder 還提供了一種創建本機進程的簡單方法,應該用它來代替所有過時的 Runtime.exec() 調用。開始重構吧!
閱讀關于 Java 的全部文章