当前位置: 首页 > 知识库问答 >
问题:

java - 如何在Java中使用ProcessBuilder运行nohup命令并保持后台运行?

景河
2024-10-07

如何在java中调用ProcessBuilder运行nohup...&格式的bash命令?

我现在有一个简单的bash脚本t.sh。这个脚本维护一个计数器,每过1s,该计数器会+1,并且将当前该计数器的值输出到标准输出上。

#!/bin/bash

counter=0

while true; do
    counter=$((counter + 1))
    echo $counter
    sleep 1
done

我可以直接使用nohup bash t.sh & 来在linux环境下运行该脚本,该脚本会在nohup.out中输出下面的预期结果

1
2
3
......

现在,我想要在Java中通过ProcessBuilder来运行该脚本。我可以使用下列方法

    @Test
    void testNohupInOne() throws IOException, InterruptedException {
        // String cmd = "nohup bash t.sh &";
        String cmd = "bash t.sh";

        String[] cmds = new String[] { "bash", "-c", cmd };
        ProcessBuilder processBuilder = new ProcessBuilder(cmds);
        Process process = processBuilder.start();
        
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            reader.lines().forEach(line -> {
                System.out.println(line);
            });
        }

        process.waitFor();
    }

上述方法可以正常在java程序的标准输出上,输出和直接在linux终端中使用nohup bash t.sh & 后在nohup.out中一样的结果。

然而,如果我们将java中使用的命令替换为bash t.sh,即将上面testNohupInOne方法的第一行取消注释,而第二行添加注释

String cmd = "nohup bash t.sh &";
// String cmd = "bash t.sh";

那么,java程序只会输出1

1

这似乎意味着,java程序会立刻结束nohup...&格式的命令的运行,而非如同预期一般将其作为后台进程持续运行。
如果我们将bash脚本中counter的初始值从0改为3,则java程序的输出会对应的变为仅有4一行。

我想要知道,为什么java程序会产生这样的行为?如果我想要在java程序中通过ProcessBuilder运行nohup...&格式的bash命令,是否有比较优雅的实现方式?

共有2个答案

罗凯
2024-10-07

机器人回答的可靠,我建议你通过Runtime.getRuntime().exec():,另外把脚本改一下

#!/bin/bash

counter=0

: > /tmp/res.dat

while true; do
    counter=$((counter + 1))
    echo $counter >> ~/tmp/res.dat
    sleep 1
done

因为你使用echo输出,这是在标准输出的,nohup会创建一个nohup.out文件接收你的输出,&将任务丢在了后台,你通过ProcessBuilder的流读取结果时,任务已经在后台运行了。

你可以先启动程序,然后去监听/tmp/res.dat的变化,这样结果就是准确的

唐珂
2024-10-07

问题解释

在Java中使用ProcessBuilder时,你遇到的问题是由于对nohup和后台运行(&)命令的理解不足导致的。当你在bash中直接执行nohup bash t.sh &时,这个命令被shell处理,它负责将进程放到后台并管理nohup的输出重定向。但是,当你在Java的ProcessBuilder中尝试这样做时,&字符并不被Java或bash -c直接理解为一个后台运行的指示符,因为bash -c通常期望的是一个完整的命令字符串,而不是一个会被shell解析的命令行。

Java中优雅的实现方式

为了在Java中通过ProcessBuilder优雅地运行一个命令(如nohup bash t.sh)并保持其在后台运行,你可以考虑以下几种方法:

  1. 直接运行不带&的nohup命令
    由于Java的ProcessBuilder会阻塞等待子进程结束(除非你另外管理子进程的线程),你可以简单地去掉&,然后让Java程序继续执行其他任务,而不需要等待这个子进程结束。

    String cmd = "nohup bash t.sh > nohup.out 2>&1 &";
    String[] cmds = {"bash", "-c", cmd.replace("&", "")}; // 移除&,因为bash -c不期望它
    ProcessBuilder processBuilder = new ProcessBuilder(cmds);
    Process process = processBuilder.start();
    
    // 如果你不需要等待这个进程结束,可以直接继续你的程序
    // 如果需要等待,可以使用 process.waitFor(); 但这通常不是后台运行的意图

注意:这里去掉了&,但在实际bash脚本中,你可能仍然希望使用它(虽然在这个特定的bash -c调用中不被需要)。如果你确实需要后台运行并且不关心nohup.out的输出,你可以让bash脚本自己处理后台化(通过脚本内部的&),但在ProcessBuilder调用中不这样做。

  1. 使用Runtime.getRuntime().exec()
    虽然ProcessBuilder是更推荐的方式,但在某些情况下,你可能想使用Runtime.getRuntime().exec(),特别是如果你需要更直接地模拟shell行为。然而,这通常不推荐,因为它更难于正确处理和避免潜在的安全风险。
  2. 分离子进程管理
    如果你的Java应用确实需要管理多个这样的后台进程,并且需要它们独立于Java进程的生命周期,你可能需要将这些命令放入到shell脚本中,并通过Java调用这个shell脚本来启动它们。然后,Java程序可以继续执行其他任务,而不需要等待这些后台进程。

结论

对于大多数情况,简单地去掉&并让Java程序继续执行其他任务,而nohupbash脚本在后台运行,是一个简单且有效的解决方案。如果你确实需要Java程序与这些后台进程交互(例如,检查它们的状态或终止它们),你可能需要实现更复杂的进程管理逻辑。

 类似资料:
  • 线程“main”java.io.ioException中出现异常:无法运行程序“java” 但是当我从终端上运行任何java命令时,它们都工作得很好。 我发现的另一件事是,当我在终端中运行命令:并使用ProcessBuilder()时,它们会显示不同的输出。即ProcessBuilder命令中不显示jdk/bin的路径。 我如何解决这个问题?

  • 问题内容: 我有以下代码行可运行批处理文件, 但是我希望它在后台运行,而不是向用户显示命令行。我该如何更改才能做到这一点? 问题在于命令窗口打开并中断程序GUI。我只希望命令窗口在执行批处理文件时不可见。 问题答案: 完全删除“开始”将完成您想要的操作(因为这就是创建窗口的原因): 我已经对此进行了测试,并且可以正常工作,当然,如果您想与命令提示符进行通信,则必须具有输入和输出流,也不要忘记您的错

  • 我正在尝试使用ProcessBuild运行cmd语句。 但是,我只能打开cmd。exe 那么如何编写此语句通过java运行cmd命令??我得到了错误,因为语句由"*"组成。如何编辑ProcessBuilder以便我可以运行语句?非常感谢

  • 在前面章节中,我们一直在说进程可以放到后台运行,这里的后台,其实指的是当前登陆终端的后台。这种情况下,当我们以远程管理服务器的方式,在远程终端执行后台命令,如果在命令尚未执行完毕时就退出登陆,那么这个后台命令还会继续执行吗? 当然不会,此命令的执行会被中断。这就引出一个问题,如果我们确实需要在远程终端执行某些后台命令,该如何执行呢?有以下 3 种方法: 把需要在后台执行的命令加入 /etc/rc.

  • 当我在终端中点击curl代码时,我得到了200,所以我假设我编写testStytch的方式到目前为止还可以。但是,一旦我试图集成到java文件中,我就会收到错误的请求响应。我现在有点不知所措。https://github.com/libetl/curl这就是我所说的转换curl代码。 这是我得到的错误。https响应代理{HTTP/1.1 400错误请求[日期:星期四,2021 23:21:424

  • 本文向大家介绍在Linux中后台运行命令,包括了在Linux中后台运行命令的使用技巧和注意事项,需要的朋友参考一下 要了解何时使用Linux,一种有用的技术是在后台运行命令。有时某些命令可能要花一些时间(例如复制大文件),或者在运行时仅接管终端窗口。例如,假设您在gedit中打开文件,则可以使用以下命令。 gedit file.txt 这样做将在gedit中打开文件,但在关闭gedit之前不允许您