Java 實現一套跨平台、高可靠的系統信息採集方案。
跨平台終端信息採集
在開發資產管理、安全審計或分佈式系統監控時,獲取終端設備的唯一標識(如 MAC 地址、磁盤序列號、CPU ID)是一項基礎且關鍵的需求。然而,不同操作系統的查詢命令各異,且 Java 原生 API 在某些場景下受限。
SystemInfoCollector 提供了一套優雅的解決方案:“Java 原生優先 + 系統命令兜底”。
- 代碼如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 終端信息採集工具類
* localIp本地IP、mac地址、pcName字段信息優先使用Java方式獲取
* 失敗則使用命令方式獲取
*
* @author xdr630
* @since 2.0.0 2025/12/24
*/
public final class SystemInfoCollectorSystemInfoCollector {
private static final Logger LOGGER = LoggerFactory.getLogger(SystemInfoCollector.class);
private static final String VERSION = "1.6.0";
private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
private static final boolean IS_WINDOWS = OS_NAME.contains("win");
private static final boolean IS_LINUX =
OS_NAME.contains("linux") || OS_NAME.contains("nix") || OS_NAME.contains("nux") || OS_NAME.contains("aix");
private static final boolean IS_MAC = OS_NAME.contains("mac");
private static final String MAC_ADDRESS_PREFIX = "MACAddress=";
private static final String SERIAL_NUMBER = "SerialNumber";
private static final String PROCESSOR_ID = "ProcessorId";
private static final String CPU_SERIAL_ALL_ZERO = "0000000000000000";
private static final String VOLUME_SERIAL_NUMBER = "VolumeSerialNumber";
private static final String FILE_SYSTEM = "FileSystem";
private static final String SIZE = "Size";
private static final int VOLUME_SERIAL_NUMBER_LENGTH = 8;
private static final int UUID_LABEL_PARTS_LENGTH = 4;
private static final String DOT = ".";
private static final String COLON = ":";
private static final String HYPHEN = "-";
private static final Pattern IPV4_PATTERN =
Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
/**
* 常見虛擬/容器/橋接/隧道網卡前綴
*/
private static final String[] VIRTUAL_IFACE_PREFIXES = {
// VirtualBox
"vbox",
// VMware
"vmnet",
// 容器虛擬網卡
"veth",
// docker bridge
"br-",
// docker0 等
"docker",
// macOS VPN / tunnel
"utun",
// macOS 本地鏈路
"llw",
// macOS AirDrop/WiFi Direct
"awdl"};
private SystemInfoCollector() {
}
/**
* 獲取插件版本-version
*
* @return 插件版本
*/
public static String getVersion() {
return VERSION;
}
/**
* 獲取系統名稱-systemName
*
* @return 系統名稱
*/
public static String getSystemName() {
return System.getProperty("os.name");
}
/**
* 判斷網卡名稱是否是 虛擬/容器/橋接網卡
*/
private static boolean isVirtualLikeInterfaceName(String ifaceName) {
if (ifaceName == null || ifaceName.isEmpty()) {
return false;
}
String name = ifaceName.toLowerCase();
for (String prefix : VIRTUAL_IFACE_PREFIXES) {
if (name.startsWith(prefix)) {
return true;
}
}
return false;
}
/**
* 判斷網卡是否是一個“合適的網卡”
* 規則:已啓動、非迴環、非虛擬、名稱不在黑名單,可選要求有 IPv4
*/
private static boolean isUsableInterface(NetworkInterface iface, boolean requireIpv4) {
try {
if (iface == null) {
return false;
}
if (!iface.isUp() || iface.isLoopback() || iface.isVirtual()) {
return false;
}
if (isVirtualLikeInterfaceName(iface.getName())) {
return false;
}
if (!requireIpv4) {
return true;
}
Enumeration<InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
String ip = addr.getHostAddress();
if (isValidIp(ip)) {
return true;
}
}
return false;
} catch (Exception e) {
LOGGER.debug("判斷網卡可用性異常: {}", iface, e);
return false;
}
}
/**
* 獲取本地IP-localIp
* 優先使用Java方式獲取,失敗則使用命令方式獲取
*/
public static String getLocalIp() {
String ip = getLocalIpByJava();
if (ip == null || ip.isEmpty()) {
LOGGER.warn("Java方式獲取IP未獲得有效結果,嘗試命令方式");
ip = getLocalIpByCommand();
}
if (ip == null || ip.isEmpty()) {
LOGGER.error("獲取本地IP失敗,Java方式與命令方式均未獲取到有效IP");
throw new RuntimeException("Failed to get localIp");
}
return ip;
}
/**
* 使用Java方式獲取本地IP,失敗返回 null
*/
private static String getLocalIpByJava() {
try {
// 獲取所有網絡接口
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface iface = interfaces.nextElement();
// 需要至少有一個 IPv4 的可用網卡
if (!isUsableInterface(iface, true)) {
continue;
}
// 獲取接口的IP地址
Enumeration<InetAddress> addresses = iface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
// 只返回IPv4地址
String ip = addr.getHostAddress();
if (isValidIp(ip)) {
return ip;
}
}
}
// 如果沒找到,返回本地主機IP
String fallback = InetAddress.getLocalHost().getHostAddress();
return isValidIp(fallback) ? fallback : null;
} catch (Exception e) {
LOGGER.debug("Java方式獲取IP異常", e);
return null;
}
}
/**
* 使用命令方式獲取本地IP,失敗返回 null
*/
private static String getLocalIpByCommand() {
try {
if (IS_WINDOWS) {
// Windows 系統:使用 WMI 查詢 IP 地址
return getWindowsLocalIp();
} else if (IS_LINUX) {
// Linux 系統:使用 ifconfig 命令
return getLinuxLocalIp();
} else if (IS_MAC) {
// MacOS 系統:使用 ifconfig 命令
return getMacLocalIp();
} else {
LOGGER.warn("不支持的操作系統獲取IP: {}", OS_NAME);
return null;
}
} catch (Exception e) {
LOGGER.debug("命令方式獲取IP異常", e);
return null;
}
}
/**
* Windows 系統:獲取本地 IP 地址
*
* @return 本地 IP 地址
*/
private static String getWindowsLocalIp() {
String command = "wmic nicconfig where IPEnabled=true get IPAddress";
return executeWindowsCommand(command, line -> {
if (line.contains(DOT)) {
String[] parts = line.split(",");
for (String part : parts) {
part = part.replaceAll("[{}\"]", "").trim();
if (isValidIp(part)) {
return part;
}
}
}
return null;
});
}
/**
* 檢查 IP 地址是否有效
*
* @param ip IP 地址
* @return 是否有效
*/
private static boolean isValidIp(String ip) {
if (ip == null) {
return false;
}
Matcher matcher = IPV4_PATTERN.matcher(ip);
return matcher.matches();
}
/**
* Linux 系統:獲取本地 IP 地址
*
* @return 本地 IP 地址
*/
private static String getLinuxLocalIp() {
String command =
"ifconfig | grep -E 'flags=|inet |broadcast ' | grep -i RUNNING -A 1 | grep 'inet ' | grep -m 1 " +
"'broadcast ' | awk '{print $2}'";
return executeCommandAndParseOutput(command, line -> line);
}
/**
* MacOS 系統:獲取本地 IP 地址
*
* @return 本地 IP 地址
*/
private static String getMacLocalIp() {
String command =
"ifconfig | grep -E 'flags=|inet |broadcast ' | grep -i RUNNING -A 1 | grep 'inet ' | grep -m 1 " +
"'broadcast ' | awk '{print $2}'";
return executeCommandAndParseOutput(command, line -> line);
}
/**
* 執行 Windows 命令並解析輸出
*
* @param command 命令
* @param outputProcessor 輸出處理函數
* @return 處理後的輸出結果
*/
private static String executeWindowsCommand(String command, Function<String, String> outputProcessor) {
try {
Process process = Runtime.getRuntime().exec(command);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
String result = outputProcessor.apply(line.trim());
if (result != null) {
return result;
}
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to execute command: " + command, e);
}
return null;
}
/**
* Linux或MacOS下執行命令並解析輸出
*
* @param command 命令
* @return 輸出結果
*/
private static String executeCommandAndParseOutput(String command, Function<String, String> outputProcessor) {
try {
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.trim().isEmpty()) {
String out = outputProcessor.apply(line.trim());
if (out != null) {
return out;
}
}
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to execute command: " + command, e);
}
return null;
}
/**
* 獲取本機 MAC 地址-mac
* 優先使用Java方式獲取MAC地址,失敗則使用命令方式獲取MAC地址
*
* @return 本機 MAC 地址
*/
public static String getMac() {
String mac = getMacByJava();
if (mac == null || mac.isEmpty()) {
LOGGER.warn("Java方式獲取MAC未獲得有效結果,嘗試命令方式");
mac = getMacByCommand();
}
if (mac == null || mac.isEmpty()) {
LOGGER.error("獲取本機MAC失敗,Java方式與命令方式均未獲取到有效MAC");
throw new RuntimeException("Failed to get MAC address");
}
return mac;
}
/**
* 使用Java方式獲取MAC地址,失敗返回 null
*/
private static String getMacByJava() {
try {
// 獲取第一個非迴環的網絡接口
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface iface = interfaces.nextElement();
// 要求是一個“有 IPv4 的可用網卡”,避免拿到一些無IP或特殊用途網卡
if (!isUsableInterface(iface, true)) {
continue;
}
// 獲取MAC地址
byte[] macBytes = iface.getHardwareAddress();
if (macBytes != null && macBytes.length > 0) {
return formatMac(macBytes);
}
}
} catch (Exception e) {
LOGGER.debug("Java方式獲取MAC異常", e);
}
return null;
}
/**
* 使用命令方式獲取MAC地址,失敗返回 null
*/
private static String getMacByCommand() {
try {
if (IS_WINDOWS) {
// Windows 系統:使用 WMI 查詢 MAC 地址
return formatMac(getWindowsMac());
} else if (IS_LINUX) {
// Linux 系統:使用 ifconfig 命令
return formatMac(getLinuxMac());
} else if (IS_MAC) {
// MacOS 系統:使用 ifconfig 命令
return formatMac(getMacOsMac());
} else {
LOGGER.warn("不支持的操作系統獲取MAC: {}", OS_NAME);
return null;
}
} catch (Exception e) {
LOGGER.debug("命令方式獲取MAC異常", e);
return null;
}
}
/**
* 格式化 MAC 地址為無分隔符的形式
*
* @param mac MAC 地址
* @return 無分隔符的 MAC 地址
*/
private static String formatMac(String mac) {
if (mac == null || mac.isEmpty()) {
return null;
}
// 移除所有分隔符(如 ":", "-")
return mac.replaceAll("[:\\-]", "");
}
/**
* 格式化MAC地址字節數組為字符串
*/
private static String formatMac(byte[] macBytes) {
StringBuilder sb = new StringBuilder();
for (byte b : macBytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
/**
* Windows 系統:獲取 MAC 地址
*
* @return MAC 地址
*/
private static String getWindowsMac() {
// 篩選出物理適配器,並且是已啓用的狀態
String command = "wmic nic where \"PhysicalAdapter=True and NetEnabled=True\" get MACAddress /format:value";
return executeWindowsCommand(command, line -> {
if (line.startsWith(MAC_ADDRESS_PREFIX) && line.length() > MAC_ADDRESS_PREFIX.length()) {
// 清除前綴
String macAddress = line.substring(MAC_ADDRESS_PREFIX.length()).trim();
return macAddress.replace(COLON, HYPHEN);
}
return null;
});
}
/**
* Linux 系統:獲取 MAC 地址
*
* @return MAC 地址
*/
private static String getLinuxMac() {
String command =
"ifconfig | grep -E 'flags=|inet |broadcast |ether ' | grep -i RUNNING -A 2 | grep -A 1 -E 'broadcast" +
" " + "|inet ' | grep -m 1 'ether ' | awk '{print $2}'";
return executeCommandAndParseOutput(command, line -> line);
}
/**
* MacOS 系統:獲取 MAC 地址
*
* @return MAC 地址
*/
private static String getMacOsMac() {
String command =
"ifconfig | grep -E 'flags=|inet |broadcast |ether ' | grep -i RUNNING -A 2 | grep -B 1 -E 'broadcast" +
" " + "|inet ' | grep -m 1 'ether ' | awk '{print $2}'";
return executeCommandAndParseOutput(command, line -> line);
}
/**
* 獲取CPU序列號-cpuSerial
*
* @return CPU 序列號
*/
public static String getCpuSerial() {
try {
if (IS_WINDOWS) {
// Windows 系統:使用 wmic 命令獲取 CPU 序列號
return getWindowsCpuSerial();
} else if (IS_LINUX) {
// Linux 系統:使用 dmidecode 命令獲取 CPU 序列號
return getLinuxCpuSerial();
} else if (IS_MAC) {
// macOS 系統:使用 system_profiler 命令獲取 CPU 序列號
return getMacCpuSerial();
} else {
throw new UnsupportedOperationException("Unsupported operating system: " + OS_NAME);
}
} catch (Exception e) {
LOGGER.error("獲取CPU序列號失敗", e);
throw new RuntimeException("Failed to get cpuSerial", e);
}
}
/**
* Windows 系統:獲取 CPU 序列號
*
* @return CPU 序列號
*/
private static String getWindowsCpuSerial() {
String command = "wmic cpu get ProcessorId";
return executeWindowsCommand(command, line -> {
if (!line.isEmpty() && !line.contains(PROCESSOR_ID)) {
return line;
}
return null;
});
}
/**
* Linux 系統:獲取 CPU 序列號
*
* @return CPU 序列號
*/
private static String getLinuxCpuSerial() {
String command = "dmidecode -t 4 | grep -m 1 ID | awk '{print $2$3$4$5$6$7$8$9}'";
return executeCommandAndParseOutput(command, line -> {
// 去掉所有空格
String cpuSerial = line.replaceAll("\\s+", "");
// 如果 CPU 序列號全為 0,則返回 null
if (CPU_SERIAL_ALL_ZERO.equals(cpuSerial)) {
return null;
}
return cpuSerial;
});
}
/**
* macOS 系統:獲取 CPU 序列號
*
* @return CPU 序列號
*/
private static String getMacCpuSerial() {
String command = "system_profiler SPHardwareDataType | grep -m 1 'Serial Number' | awk '{print $4}'";
return executeCommandAndParseOutput(command, line -> {
// 去掉所有空格
return line.trim().replaceAll("\\s+", "");
});
}
/**
* 獲取硬盤序列號-hardSerial
*/
public static String getHardSerial() {
try {
if (IS_WINDOWS) {
// Windows 系統
return getWindowsHardSerial();
} else if (IS_LINUX) {
// Linux 系統
return getLinuxHardSerial();
} else if (IS_MAC) {
// macOS 系統
return getMacHardSerial();
} else {
throw new UnsupportedOperationException("Unsupported operating system: " + OS_NAME);
}
} catch (Exception e) {
LOGGER.error("獲取硬盤序列號失敗", e);
throw new RuntimeException("Failed to get hardSerial", e);
}
}
/**
* Windows 系統:獲取硬盤序列號
*
* @return 硬盤序列號,如:6479_A75B_B090_09E0
*/
private static String getWindowsHardSerial() {
String command = "wmic diskdrive get serialnumber";
return executeWindowsCommand(command, line -> {
if (!line.trim().isEmpty() && !line.contains(SERIAL_NUMBER)) {
// 去掉末尾的點(如果存在)
return line.trim().endsWith(DOT) ? line.trim().substring(0, line.length() - 1) : line.trim();
}
return null;
});
}
/**
* Linux 系統:獲取硬盤序列號
*
* @return 硬盤序列號,如:ac7b3398-162e-4775-b
*/
private static String getLinuxHardSerial() {
// Linux amd 執行後:SERIAL=""
String command =
"lsblk -p -P -o NAME,SERIAL,UUID,TYPE,MOUNTPOINT | grep -i boot -B 1 | grep -i disk | awk '{print $2}'";
return executeCommandAndParseOutput(command, line -> {
String result = line.trim().replace("SERIAL=", "").replace("\"", "");
// 去掉末尾的點(如果存在)
if (result.endsWith(DOT)) {
result = result.substring(0, result.length() - 1);
}
// 如果序列號為空,返回 null
return result.isEmpty() ? null : result;
});
}
/**
* macOS 系統:獲取硬盤序列號
*
* @return 硬盤序列號
*/
private static String getMacHardSerial() {
String command = "system_profiler SPHardwareDataType | grep -m 1 'Hardware UUID' | awk '{print $3}'";
return executeCommandAndParseOutput(command, line -> {
String result = line.trim();
// 去掉末尾的點(如果存在)
if (result.endsWith(DOT)) {
result = result.substring(0, result.length() - 1);
}
return result;
});
}
/**
* 獲取系統盤盤符-drive
*
* @return 系統盤盤符,如:C 或 /dev/sda1
* @throws RuntimeException 獲取失敗時拋出異常
*/
public static String getDrive() {
try {
if (IS_WINDOWS) {
// Windows 系統
return getWindowsDrive();
} else if (IS_LINUX) {
// Linux 系統
return getLinuxDrive();
} else if (IS_MAC) {
// macOS 系統
return getMacDrive();
} else {
throw new UnsupportedOperationException("Unsupported operating system: " + OS_NAME);
}
} catch (Exception e) {
LOGGER.error("獲取系統盤盤符失敗", e);
throw new RuntimeException("Failed to get drive", e);
}
}
/**
* Windows 系統:獲取盤符
*
* @return 盤符,如:C
*/
private static String getWindowsDrive() {
// 獲取系統盤盤符(如 C:)
String systemDrive = System.getenv("SystemDrive");
if (systemDrive == null || systemDrive.isEmpty()) {
LOGGER.error("SystemDrive environment variable is empty");
return null;
}
// 去掉冒號
return systemDrive.replace(COLON, "");
}
/**
* Linux 系統:獲取盤符
*
* @return 盤符,如:/dev/sda1
*/
private static String getLinuxDrive() {
String command =
"lsblk -p -P -o NAME,PARTUUID,FSTYPE,SIZE,UUID,TYPE,MOUNTPOINT | grep -E '/boot\"$' | grep -i part | " +
"awk " + "'{print $1,$2,$3,$4}'";
return executeCommandAndParseOutput(command, line -> {
String[] split = line.split("\"");
return split.length > 1 ? split[1] : null;
});
}
/**
* macOS 系統:獲取盤符
*
* @return 盤符
*/
private static String getMacDrive() {
String command = "system_profiler SPSoftwareDataType | grep -m 1 'Boot Volume'";
return executeCommandAndParseOutput(command, line -> {
String[] split = line.split(": ");
return split.length > 1 ? split[1] : null;
});
}
/**
* 獲取系統盤分區格式-fileSystem
*
* @return 系統盤分區格式,如:NTFS 或 xf4
* @throws RuntimeException 如果無法獲取分區格式
*/
public static String getFileSystem() {
try {
if (IS_WINDOWS) {
// Windows 系統
return getWindowsFileSystem();
} else if (IS_LINUX) {
// Linux 系統
return getLinuxFileSystem();
} else if (IS_MAC) {
// macOS 系統
return getMacFileSystem();
} else {
throw new UnsupportedOperationException("Unsupported operating system: " + OS_NAME);
}
} catch (Exception e) {
LOGGER.error("獲取系統盤分區格式失敗", e);
throw new RuntimeException("Failed to get fileSystem", e);
}
}
/**
* Windows 系統:獲取分區格式
*
* @return 分區格式,如:NTFS
*/
private static String getWindowsFileSystem() {
// 獲取系統盤盤符(如 C:)
String systemDrive = System.getenv("SystemDrive");
if (systemDrive == null || systemDrive.isEmpty()) {
LOGGER.error("SystemDrive environment variable is empty");
return null;
}
// 獲取系統盤的分區信息
String command = "wmic logicaldisk where deviceid='" + systemDrive + "' get filesystem";
return executeWindowsCommand(command, line -> {
if (!line.isEmpty() && !line.contains(FILE_SYSTEM)) {
return line;
}
return null;
});
}
/**
* Linux 系統:獲取分區格式
*
* @return 分區格式
*/
private static String getLinuxFileSystem() {
String command =
"lsblk -p -P -o NAME,PARTUUID,FSTYPE,SIZE,UUID,TYPE,MOUNTPOINT | grep -E '/boot\"$' | grep -i part | " +
"awk " + "'{print $1,$2,$3,$4}'";
return executeCommandAndParseOutput(command, line -> {
String[] split = line.split("\"");
return split.length > 5 ? split[5] : null;
});
}
/**
* macOS 系統:獲取分區格式
*
* @return 分區格式
*/
private static String getMacFileSystem() {
String command = "system_profiler SPStorageDataType | grep -w -A 5 -B 1 'Mount Point: /'";
return executeCommandAndParseOutput(command, line -> {
String number = "";
String[] lines = line.split("\n");
for (String l : lines) {
l = l.trim();
if (l.startsWith("File System:")) {
String[] split = l.split(" ");
if (split.length > 2) {
number = split[2];
}
}
}
return number.isEmpty() ? null : number;
});
}
/**
* 獲取系統盤分區容量
*
* @return 系統盤分區容量,如:119G
* @throws RuntimeException 如果無法獲取分區容量
*/
public static String getPartitionSize() {
try {
if (IS_WINDOWS) {
// Windows 系統
return getWindowsPartitionSize();
} else if (IS_LINUX) {
// Linux 系統
return getLinuxPartitionSize();
} else if (IS_MAC) {
// macOS 系統
return getMacPartitionSize();
} else {
throw new UnsupportedOperationException("Unsupported operating system: " + OS_NAME);
}
} catch (Exception e) {
LOGGER.error("獲取系統盤分區容量失敗", e);
throw new RuntimeException("Failed to get partition size", e);
}
}
/**
* Windows 系統:獲取分區容量
*
* @return 分區容量
*/
private static String getWindowsPartitionSize() {
// 獲取系統盤盤符(如 C:)
String systemDrive = System.getenv("SystemDrive");
if (systemDrive == null || systemDrive.isEmpty()) {
LOGGER.error("SystemDrive environment variable is empty");
return null;
}
// 獲取系統盤的分區信息
String command = "wmic logicaldisk where deviceid='" + systemDrive + "' get size";
return executeWindowsCommand(command, line -> {
if (!line.isEmpty() && !line.contains(SIZE)) {
long sizeBytes = Long.parseLong(line);
return (sizeBytes / 1024 / 1024 / 1024) + "G";
}
return null;
});
}
/**
* Linux 系統:獲取分區容量
*
* @return 分區容量
*/
private static String getLinuxPartitionSize() {
String command =
"lsblk -p -P -o NAME,PARTUUID,FSTYPE,SIZE,UUID,TYPE,MOUNTPOINT | grep -E '/boot\"$' | grep -i part | " +
"awk " + "'{print $1,$2,$3,$4}'";
return executeCommandAndParseOutput(command, output -> {
String[] split = output.split("\"");
return split.length > 7 ? split[7] : null;
});
}
/**
* macOS 系統:獲取分區容量
*
* @return 分區容量
*/
private static String getMacPartitionSize() {
String command = "system_profiler SPStorageDataType | grep -w -A 5 -B 1 'Mount Point: /'";
return executeCommandAndParseOutput(command, line -> {
String size = "";
String[] lines = line.split("\n");
for (String l : lines) {
l = l.trim();
if (l.startsWith("Capacity:")) {
String[] split = l.split(" ");
if (split.length > 2) {
size = split[1] + "G";
}
}
}
return size;
});
}
/**
* 獲取系統盤卷標號-systemDisk
*
* @return 系統盤卷標號
*/
public static String getSystemDisk() {
try {
if (IS_WINDOWS) {
// Windows 系統
return getWindowsSystemDisk();
} else if (IS_LINUX) {
// Linux 系統
return getLinuxSystemDisk();
} else if (IS_MAC) {
// macOS 系統
return getMacSystemDisk();
} else {
throw new UnsupportedOperationException("Unsupported operating system: " + OS_NAME);
}
} catch (Exception e) {
LOGGER.error("獲取系統盤卷標號失敗", e);
throw new RuntimeException("Failed to get systemDisk", e);
}
}
/**
* Windows 系統:獲取系統盤卷標號
*
* @return 系統盤卷標號,格式為 "XXXX-XXXX",如:8AD0-CC8B
*/
private static String getWindowsSystemDisk() {
// 獲取系統盤盤符(如 C:)
String systemDrive = System.getenv("SystemDrive");
if (systemDrive == null || systemDrive.isEmpty()) {
LOGGER.error("SystemDrive environment variable is empty");
return null;
}
// 獲取系統盤的卷標號
String command = "wmic logicaldisk where deviceid='" + systemDrive + "' get VolumeSerialNumber";
return executeWindowsCommand(command, line -> {
if (!line.isEmpty() && !line.contains(VOLUME_SERIAL_NUMBER)) {
if (line.length() == VOLUME_SERIAL_NUMBER_LENGTH) {
// 格式化為 XXXX-XXXX
return line.substring(0, 4) + HYPHEN + line.substring(4);
}
}
return null;
});
}
/**
* Linux 系統:獲取系統盤卷標號
*
* @return 系統盤卷標號
*/
private static String getLinuxSystemDisk() {
// 使用 lsblk 命令獲取系統盤卷標號
// Linux amd執行後:UUID="" LABEL=""
String command =
"lsblk -p -P -o NAME,UUID,LABEL,TYPE,MOUNTPOINT | grep -i boot -B 1 | grep -i disk | awk '{print $2," +
"$3}'";
return executeCommandAndParseOutput(command, line -> {
String[] parts = line.trim().split("\"");
if (parts.length >= UUID_LABEL_PARTS_LENGTH) {
// UUID
String uuid = parts[1];
// LABEL
String label = parts[3];
// 返回 UUID 或 LABEL
return !uuid.isEmpty() ? uuid : label;
}
return null;
});
}
/**
* macOS 系統:獲取系統盤卷標號
*
* @return 系統盤卷標號
*/
private static String getMacSystemDisk() {
String command = "system_profiler SPStorageDataType | grep -w -A 5 -B 1 'Mount Point: /'";
return executeCommandAndParseOutput(command, line -> {
String number = "";
String[] lines = line.split("\n");
for (String l : lines) {
l = l.trim();
if (l.startsWith("Volume UUID:")) {
String[] split = l.split(" ");
if (split.length > 2) {
number = split[2];
}
}
}
return number.isEmpty() ? null : number;
});
}
/**
* 獲取PC終端設備名稱-pcName
*
* @return PC終端設備名稱
*/
public static String getPcName() {
String pcName = getPcNameByJava();
if (pcName == null || pcName.isEmpty()) {
LOGGER.warn("Java方式獲取PC終端設備名稱未獲得有效結果,嘗試命令方式");
pcName = getPcNameByCommand();
}
if (pcName == null || pcName.isEmpty()) {
LOGGER.error("獲取PC終端設備名稱失敗,Java方式與命令方式均未獲取到有效名稱");
throw new RuntimeException("Failed to get pcName");
}
return pcName;
}
/**
* 使用Java方式獲取PC終端設備名稱-pcName
*
* @return PC終端設備名稱,失敗返回 null
*/
private static String getPcNameByJava() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
LOGGER.debug("Java方式獲取PC終端設備名稱異常", e);
return null;
}
}
/**
* 使用命令方式獲取PC終端設備名稱-pcName
*
* @return PC終端設備名稱,失敗返回 null
*/
public static String getPcNameByCommand() {
String command = "hostname";
try {
if (IS_WINDOWS) {
// Windows 系統:使用 hostname 命令獲取設備名稱
return executeWindowsCommand(command, line -> line.isEmpty() ? null : line);
} else if (IS_LINUX) {
// Linux 系統:使用 hostname 命令獲取設備名稱
return executeCommandAndParseOutput(command, line -> line);
} else if (IS_MAC) {
// MacOS 系統:使用 scutil 命令獲取設備名稱
return executeCommandAndParseOutput("scutil --get ComputerName", line -> line);
} else {
LOGGER.warn("不支持的操作系統獲取PC終端設備名稱: {}", OS_NAME);
return null;
}
} catch (Exception e) {
LOGGER.debug("命令方式獲取PC終端設備名稱異常", e);
return null;
}
}
/**
* 獲取PC終端設備序列號(僅 Mac 系統有,其他系統返回 "null")
*
* @return PC 終端設備序列號,如果獲取失敗或非 Mac 系統則返回 "null"
*/
public static String getPcSerial() {
if (!IS_MAC) {
// 非 Mac 系統直接返回 "null"
return "null";
}
try {
// MacOS 系統:使用 system_profiler 命令獲取設備序列號
String command = "system_profiler SPHardwareDataType | grep -m 1 'Provisioning UDID' | awk '{print $3}'";
return executeCommandAndParseOutput(command, line -> line);
} catch (Exception e) {
LOGGER.error("獲取PC終端設備序列號失敗", e);
throw new RuntimeException("Failed to get pcSerial on MacOS.", e);
}
}
}
核心設計理念
該工具類的核心邏輯遵循以下優先級:
- Java 原生 API:跨平台性好,執行效率高,作為首選方案。
- 系統原生命令:當 Java API 無法獲取深層硬件信息(如磁盤序列號)或執行失敗時,根據識別到的操作系統(Windows/Linux/macOS)自動調用底層命令。
主要功能特性
1. 智能的網絡接口過濾
獲取 IP 或 MAC 地址時,最頭疼的就是搜出一堆 vbox、docker 或 utun 等虛擬網卡信息。SystemInfoCollector 內置了黑名單過濾機制:
- 自動識別虛擬網卡:排除常見容器(Docker)、虛擬機(VMware/VirtualBox)及隧道網卡。
- 狀態校驗:僅針對已啓動(Up)且非迴環(Loopback)的物理網卡進行信息採集。
2. 多維度的硬件識別
工具類不僅能獲取基礎信息,還能深入挖掘硬件指紋:
- CPU 序列號:通過
wmic(Win)、dmidecode(Linux) 或system_profiler(Mac) 獲取。 - 硬盤序列號:精確獲取物理硬盤的唯一標識。
- 系統盤詳情:包括盤符、分區格式(NTFS/APFS等)、總容量以及卷標序列號。
3. 強大的跨平台兼容性
代碼內部通過 OS_NAME 常量實現了對主流系統的全覆蓋:
- Windows: 利用
wmic命令進行底層查詢。 - Linux: 結合
lsblk、ifconfig和awk進行文本解析。 - macOS: 使用特有的
system_profiler和scutil工具。
技術亮點:命令執行與結果解析
為了保證代碼的可維護性,工具類採用了一個 函數式接口(Function) 來處理命令輸出。這種設計將“執行過程”與“結果過濾”解耦:
// 以 Windows 獲取磁盤序列號為例
private static String getWindowsHardSerial() {
String command = "wmic diskdrive get serialnumber";
return executeWindowsCommand(command, line -> {
if (!line.trim().isEmpty() && !line.contains(SERIAL_NUMBER)) {
return line.trim(); // 具體的過濾邏輯由 Lambda 表達式完成
}
return null;
});
}
如何使用?
由於所有方法均設計為 static,集成非常簡單,無需實例化:
public class SystemInfoCollectorTest {
public static void main(String[] args) {
System.out.println("version: " + SystemInfoCollector.getVersion());
System.out.println("systemName: " + SystemInfoCollector.getSystemName());
System.out.println("localIp: " + SystemInfoCollector.getLocalIp());
System.out.println("mac: " + SystemInfoCollector.getMac());
System.out.println("cpuSerial: " + SystemInfoCollector.getCpuSerial());
System.out.println("hardSerial: " + SystemInfoCollector.getHardSerial());
System.out.println("drive: " + SystemInfoCollector.getDrive());
System.out.println("fileSystem: " + SystemInfoCollector.getFileSystem());
System.out.println("partitionSize: " + SystemInfoCollector.getPartitionSize());
System.out.println("systemDisk: " + SystemInfoCollector.getSystemDisk());
System.out.println("pcName: " + SystemInfoCollector.getPcName());
System.out.println("pcSerial: " + SystemInfoCollector.getPcSerial());
}
}

總結與注意事項
SystemInfoCollector 是一個封裝嚴密、容錯性強的工具類,非常適合需要快速集成硬件採集功能的 Java 項目。
tips:
- 在 Linux 環境下,某些硬件命令(如
dmidecode)可能需要sudo權限才能獲取完整信息。- 工具類默認對 IP 地址進行了 IPv4 格式校驗,若環境僅支持 IPv6,需微調正則表達式。