Windows环境下使用Java获取进程PID:OSHI库函数失效、自定义代码缺陷及父PID/带空格进程PID获取方案咨询
我来帮你搞定这几个问题——你的思路没问题,但tasklist的输出格式和多实例场景确实有坑,咱们一步步优化,顺便也说说OSHI的正确用法:
首先解决你的现有代码问题
你的代码主要卡在带空格的进程名匹配和多实例PID提取上,加上tasklist默认不输出父PID,所以咱们从这几点改:
1. 改用CSV格式解析tasklist输出
tasklist /FO CSV /V会输出带双引号包裹的CSV格式详细信息,既避免空格拆分进程名,还能拿到父进程ID(PPID)。然后用正则提取字段,就不会出错了。
2. 优化后的完整代码(无额外依赖)
import java.io.IOException; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ProcessFinder { // 正则匹配CSV行,捕获进程名、PID、PPID(兼容中英文系统输出) private static final Pattern TASKLINE_PATTERN = Pattern.compile( "\"([^\"]+)\",\"([^\"]+)\".*?\"([^\"]+)\"$"); public static String getProcessPIDs(String processName, boolean... ignoreCase) { boolean ignoreLetterCase = ignoreCase.length > 0 ? ignoreCase[0] : true; StringBuilder pidResult = new StringBuilder(); // 启动tasklist,输出CSV格式的详细信息 try (Process process = new ProcessBuilder("tasklist.exe", "/FO", "CSV", "/V").start(); Scanner scanner = new Scanner(process.getInputStream(), "UTF-8")) { // 跳过前两行表头 int lineNum = 0; while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); lineNum++; if (lineNum <= 2 || line.isEmpty()) continue; Matcher matcher = TASKLINE_PATTERN.matcher(line); if (matcher.find()) { String currentProcName = matcher.group(1); String pid = matcher.group(2); // String ppid = matcher.group(3); // 父进程ID,需要的话可以用 // 匹配进程名(支持大小写忽略) boolean isMatch = ignoreLetterCase ? currentProcName.toLowerCase().contains(processName.toLowerCase()) : currentProcName.contains(processName); if (isMatch) { if (pidResult.length() > 0) pidResult.append(", "); pidResult.append(pid); // 如果要带PPID,改成:pidResult.append(pid).append("(PPID:").append(ppid).append(")"); } } } } catch (IOException e) { e.printStackTrace(); return ""; } return pidResult.toString(); } // 额外提供:返回PID与PPID的映射 public static java.util.Map<String, String> getPIDAndPPID(String processName, boolean... ignoreCase) { boolean ignoreLetterCase = ignoreCase.length > 0 ? ignoreCase[0] : true; java.util.Map<String, String> pidPpidMap = new java.util.HashMap<>(); try (Process process = new ProcessBuilder("tasklist.exe", "/FO", "CSV", "/V").start(); Scanner scanner = new Scanner(process.getInputStream(), "UTF-8")) { int lineNum = 0; while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); lineNum++; if (lineNum <= 2 || line.isEmpty()) continue; Matcher matcher = TASKLINE_PATTERN.matcher(line); if (matcher.find()) { String currentProcName = matcher.group(1); String pid = matcher.group(2); String ppid = matcher.group(3); boolean isMatch = ignoreLetterCase ? currentProcName.toLowerCase().contains(processName.toLowerCase()) : currentProcName.contains(processName); if (isMatch) pidPpidMap.put(pid, ppid); } } } catch (IOException e) { e.printStackTrace(); } return pidPpidMap; } public static void main(String[] args) { // 测试带空格的进程 System.out.println("Edge PIDs: " + getProcessPIDs("Microsoft Edge.exe")); // 测试多实例Chrome System.out.println("Chrome PIDs: " + getProcessPIDs("chrome.exe")); // 测试获取PID和PPID getPIDAndPPID("chrome.exe").forEach((pid, ppid) -> System.out.printf("Chrome PID: %s, PPID: %s%n", pid, ppid)); } }
改动说明
- CSV格式输出:
/FO CSV让进程名被双引号包裹,不会被空格拆分;/V参数新增父进程ID字段。 - 正则提取:用正则精准捕获每个字段,避免
split("\\s+")带来的拆分错误。 - 多实例支持:现在每匹配到一个进程就追加PID,自动用逗号分隔,多实例场景完全没问题。
更推荐的跨平台方案:用OSHI正确实现
你之前说OSHI的getProcessID不好用,大概率是没找对方法。OSHI本身就是跨平台的进程信息库,直接遍历进程列表更可靠,还能轻松拿到PPID:
import oshi.SystemInfo; import oshi.software.os.OSProcess; import oshi.software.os.OperatingSystem; import java.util.List; public class OshiProcessHelper { public static String getProcessPIDs(String processName, boolean... ignoreCase) { boolean ignoreLetterCase = ignoreCase.length > 0 ? ignoreCase[0] : true; StringBuilder pidResult = new StringBuilder(); SystemInfo systemInfo = new SystemInfo(); OperatingSystem os = systemInfo.getOperatingSystem(); List<OSProcess> processes = os.getProcesses(); for (OSProcess proc : processes) { String procName = proc.getName(); boolean isMatch = ignoreLetterCase ? procName.toLowerCase().contains(processName.toLowerCase()) : procName.contains(processName); if (isMatch) { if (pidResult.length() > 0) pidResult.append(", "); pidResult.append(proc.getProcessID()); // 直接拿父进程ID:proc.getParentProcessID() } } return pidResult.toString(); } public static void main(String[] args) { System.out.println("Chrome PIDs via OSHI: " + getProcessPIDs("chrome.exe")); } }
这个方案不需要依赖Windows的tasklist,在Linux、macOS上也能运行,而且API更直观,直接通过proc.getParentProcessID()就能拿到父进程ID,比调用系统命令靠谱多了。
内容的提问来源于stack exchange,提问作者Shankypro
相关产品推荐
相关产品推荐

