如何在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命令,是否有比较优雅的实现方式?
机器人回答的可靠,我建议你通过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
的变化,这样结果就是准确的
在Java中使用ProcessBuilder
时,你遇到的问题是由于对nohup
和后台运行(&
)命令的理解不足导致的。当你在bash中直接执行nohup bash t.sh &
时,这个命令被shell处理,它负责将进程放到后台并管理nohup
的输出重定向。但是,当你在Java的ProcessBuilder
中尝试这样做时,&
字符并不被Java或bash -c
直接理解为一个后台运行的指示符,因为bash -c
通常期望的是一个完整的命令字符串,而不是一个会被shell解析的命令行。
为了在Java中通过ProcessBuilder
优雅地运行一个命令(如nohup bash t.sh
)并保持其在后台运行,你可以考虑以下几种方法:
直接运行不带&
的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
调用中不这样做。
ProcessBuilder
是更推荐的方式,但在某些情况下,你可能想使用Runtime.getRuntime().exec()
,特别是如果你需要更直接地模拟shell行为。然而,这通常不推荐,因为它更难于正确处理和避免潜在的安全风险。对于大多数情况,简单地去掉&
并让Java程序继续执行其他任务,而nohup
和bash
脚本在后台运行,是一个简单且有效的解决方案。如果你确实需要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之前不允许您