top
Loading...
馴服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

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 的全部文章
作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗