我想要通过命令行执行一个程序 类似于 Java -jar xxx.jar,然后获得该程序的进程 ID,并稍后通过进程 ID 判断该进程是否存活?但是在系统中找不到我之前获得的进程号,求帮助? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gzk329
V2EX    Java

我想要通过命令行执行一个程序 类似于 Java -jar xxx.jar,然后获得该程序的进程 ID,并稍后通过进程 ID 判断该进程是否存活?但是在系统中找不到我之前获得的进程号,求帮助?

  •  
  •   gzk329 2022-09-28 00:47:34 +08:00 2450 次点击
    这是一个创建于 1186 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我想要通过命令行执行一个程序 类似于 java -jar xxx.jar,然后获得该程序的进程 ID ,并稍后通过进程 ID 判断该进程是否存活。

    //start a process String command = "..."; ProcessBuilder pb = new ProcessBuilder(command); Process process = pb.start(); 
    //get the pid of process if (System.getProperty("os.name").toLowerCase().contains("mac")) { Class<?> clazz = Class.forName("java.lang.UNIXProcess"); field = clazz.getDeclaredField("pid"); ReflectionUtils.makeAccessible(field); pid = (Integer) field.get(process); } 

    最后我希望通过进程 id 判断该进程是否存活,但是我在查进程的时候发现 根本查不到我的那个进程号

    // judge the process is alive or not. By pid. if (System.getProperty(Constants.SYSTEM_NAME).toLowerCase().contains("linux") || System.getProperty(Constants.SYSTEM_NAME).toLowerCase().contains("mac")) { process = RuntimeUtil.exec(BIN_BASH + " -c" + " ps -elf | grep " + pid); } if(process != null){ String line; try(InputStream in = process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in,StandardCharsets.UTF_8))){ while((line = br.readLine()) != null){ if(line.contains(pid)){ //输出流中读不到我之前获得的那个进程 ID return true; } } } catch (IOException e) { //exception handle } } 
    ysc3839
        1
    ysc3839  
       2022-09-28 06:03:01 +08:00 via Android
    感觉是 RuntimeUtil.exec 的问题。
    先说正常的解法:判断 PID 对应的进程是否存在应该用更底层的 API ,比如 Linux 下读 /proc/<PID>,而不应该用命令行程序来处理。我相信 Java 有现成的支持多平台的库,完全不需要自己写。
    再解释你遇到的问题:首先类 Unix 系统的进程参数是字符串数组而不是一个字符串,比如你执行 ps -elf ,ps 进程接收到的参数一般是["ps", "-elf"].因此在执行单个字符串的“命令”时,肯定要有个程序先解析成字符串数组再传递给目标进程。
    在 shell 中执行的话,是由 shell 进行解析的,但是用别的语言提供的 API ,就得看文档了解清楚是怎么解析的了。根据 Java 的文档,单个字符串的 Runtime.exec 是用 StringTokenizer 来解析的 https://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exec(java.lang.String,%20java.lang.String[],%20java.io.File)
    而 StringTokenizer 则是按" \t\n\r\f"分割 https://docs.oracle.com/javase/7/docs/api/java/util/StringTokenizer.html#StringTokenizer(java.lang.String)
    所以你传进去的字符串最终应该是被解析成了[BIN_BASH, "-c", "ps", "-elf", "|", "grep", pid]
    而 bash -c 执行的是 -c 后面那个参数,也就等于执行 ps 。你可以试试在 shell 里执行 bash -c echo 1 和 bash -c 'echo 1' 看看有什么区别。
    顺带一提,Windows 下进程参数是一个字符串而不是字符串数组,上述逻辑不适用于 Windows 。
    ysc3839
        2
    ysc3839  
       2022-09-28 06:12:31 +08:00 via Android
    简单搜索可得知 Java 有内置 ProcessHandle
    https://docs.oracle.com/javase/9/docs/api/java/lang/ProcessHandle.html
    不过是从 Java 9 才有的
    jorneyr
        3
    jorneyr  
       2022-09-28 08:25:18 +08:00
    Java Process 不支持管道吧,有管道的命令我一般都是写入临时 shell 文件,然后执行 shell 文件。
    jorneyr
        4
    jorneyr  
       2022-09-28 08:27:25 +08:00
    ```java
    package cmd;

    import org.apache.commons.exec.CommandLine;
    import org.apache.commons.exec.DefaultExecutor;
    import org.apache.commons.exec.ExecuteWatchdog;
    import org.apache.commons.exec.PumpStreamHandler;

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Path;

    /**
    * 生成临时 shell 脚本并执行
    */
    public class ExecTempShellScript {
    public static void main(String[] args) throws IOException {
    // 1. 生成临时脚本文件
    // 2. 命令写入脚本文件
    // 3. 执行脚本
    // 4. 删除临时脚本文件

    String command = "ls -l /Users/biao";
    Path path = Files.createTempFile("mongo-", ".sh");
    Files.write(path, command.getBytes(StandardCharsets.UTF_8));
    System.out.println(path);

    try {
    execSh(path.toString());
    } finally {
    Files.delete(path);
    }
    }

    public static void execSh(String path) throws IOException {
    CommandLine cmdLine = CommandLine.parse("sh " + path);
    DefaultExecutor executor = new DefaultExecutor();
    executor.setExitValues(null);

    ExecuteWatchdog watchdog = new ExecuteWatchdog(60000);
    executor.setWatchdog(watchdog);

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
    PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream,errorStream);

    executor.setStreamHandler(streamHandler);
    executor.execute(cmdLine);

    // 获取程序外部程序执行结果
    String out = outputStream.toString("UTF-8");
    String error = errorStream.toString("UTF-8");

    // 处理结果
    System.out.println("==== ok ====");
    System.out.println(out);
    System.out.println("==== error ====");
    System.out.println(error);
    }
    }
    ```
    julyclyde
        5
    julyclyde  
       2022-09-28 14:02:12 +08:00
    我觉得可能是需求有问题
    (当然并不排除你的解法也有问题)

    如果你想要亲自做一个“运行一个程序然后监控它”,那么,选择自己做就已经错了
    iminto
        6
    iminto  
       2022-09-28 14:46:28 +08:00 via Android
    代码就错了,Java 执行 shell 命令不支持特殊字符的
    iminto
        7
    iminto  
       2022-09-28 14:47:28 +08:00 via Android
    需要转成字符串数组传递,百度一下甚至都能知道
    hahaha777
        8
    hahaha777  
       2022-09-30 13:50:32 +08:00
    RuntimeUtil.exec ,参数可能有问题。用数组。
    gzk329
        9
    gzk329  
    OP
       2022-09-30 17:41:48 +08:00
    @ysc3839 感谢回答 基本已经解决我的问题了 谢谢
    我用的这个是 RuntimeUtil.exec()是 hutools 提供的 api 可能是我没用明白
    换成 jdk 提供的 Runtime.getRuntime().exec(commands) 按照您说的正确方式传参就没问题
    new ProcessBuilder(command, arg1, arg2)

    但是我还有一个问题 我在 java 程序中起的进程算是子进程吗? 是 ps -elf 这类命令是查不到子进程吗 我发现我起一个进程 拿到进程号 用 ps -elf 就查不到 如果是 linux 读 /proc 能读到,mac 使用 lsof -p 也能读到
    gzk329
        10
    gzk329  
    OP
       2022-09-30 18:33:48 +08:00
    @gzk329 没问题了 是我又搞错了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2819 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 03:49 PVG 11:49 LAX 19:49 JFK 22:49
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86