You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 19:32:31