stdout/stderr 導致緩衝區滿阻塞。| 能力 | Runtime.exec | ProcessBuilder |
|---|---|---|
| 命令構建 | String 或 String[] | List |
| 環境變量 | 繼承父進程,不可改 | environment() 返回 Map 可增刪改 |
| 工作目錄 | 不可指定 | directory(File) 指定 |
| 輸入/輸出重定向 | 手動管道 | redirectInput/Output/Error(File/ProcessBuilder.Redirect) |
| 啟動方式 | 立即啟動 | start() 延遲啟動,可復用配置 |
import java.io.*;
import java.util.*;
public class MacAddressExtractor {
public static List getPhysicalAddresses() {
List addresses = new ArrayList<>();
ProcessBuilder pb = new ProcessBuilder(
System.getProperty("os.name").toLowerCase().startsWith("win")
? new String[]{"ipconfig", "/all"}
: new String[]{"ifconfig", "-a"}
);
// 可選:設置環境變量、工作目錄
// pb.environment().put("LANG", "C");
// pb.directory(new File("/tmp"));
try {
Process process = pb.start();
// 異步讀取 stdout/stderr 避免死鎖 (Java 8+ 可用 CompletableFuture)
String output = readStream(process.getInputStream());
int exitCode = process.waitFor();
if (exitCode != 0) {
String err = readStream(process.getErrorStream());
System.err.println("命令執行失敗: " + err);
return addresses;
}
parseAddresses(output, addresses);
} catch (IOException | InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
return addresses;
}
private static String readStream(InputStream is) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) sb.append(line).append('\n');
return sb.toString();
}
}
private static void parseAddresses(String output, List addresses) {
// Windows: "Physical Address. . . . . . . . . : 00-11-22-33-44-55"
// Linux: "ether 00:11:22:33:44:55 "
String regex = System.getProperty("os.name").toLowerCase().startsWith("win")
? "Physical Address[.\\s]*:\\s*([0-9A-F-]{17})"
: "ether\\s+([0-9a-f:]{17})";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(regex, java.util.regex.Pattern.CASE_INSENSITIVE);
java.util.regex.Matcher m = p.matcher(output);
while (m.find()) addresses.add(m.group(1).toUpperCase().replace('-', ':'));
}
public static void main(String[] args) {
List macs = getPhysicalAddresses();
for (String mac : macs) System.out.printf("物理網卡地址: %s%n", mac);
}
}
// 管道鏈:ps -ef | grep java
ProcessBuilder pb1 = new ProcessBuilder("ps", "-ef");
ProcessBuilder pb2 = new ProcessBuilder("grep", "java");
pb2.redirectInput(ProcessBuilder.Redirect.PIPE); // 將 stdout 作為下一個 stdin
Process p1 = pb1.start();
Process p2 = pb2.start();
// 需手動將 p1.getInputStream() 複製到 p2.getOutputStream() (Java 9+ 支援 pb1.startPipeline(List))
// 異步非阻塞 (Java 8+)
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
try { return readStream(pb.start().getInputStream()); }
catch (IOException e) { throw new UncheckedIOException(e); }
});
future.thenAccept(System.out::println).exceptionally(e -> { e.printStackTrace(); return null; });
stdout/stderr 至 EOF,否則子進程阻塞導致 waitFor() 永不返回。process.waitFor() 非 0 表示異常,需讀取 errorStream 診斷。List 參數化傳遞。try-with-resources 管理 InputStream,Java 9+ Process 實現 AutoCloseable 可自動銷毀子進程。ProcessBuilder 以流式 API、環境隔離、重定向支援全面超越 Runtime.exec,是 JDK 5+ 執行外部進程的標準選擇。配合 Java 8+ CompletableFuture、Java 9+ startPipeline/ProcessHandle,可構建健壯、非阻塞、跨平台的進程編排能力。