如何从 Java 程序启动完全独立的进程?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/931536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I launch a completely independent process from a Java program?
提问by Lawrence Dol
I am working on a program written in Java which, for some actions, launches external programs using user-configured command lines. Currently it uses Runtime.exec()
and does not retain the Process
reference (the launched programs are either a text editor or archive utility, so no need for the system in/out/err streams).
我正在开发一个用 Java 编写的程序,对于某些操作,它使用用户配置的命令行启动外部程序。目前它使用Runtime.exec()
并且不保留Process
引用(启动的程序是文本编辑器或存档实用程序,因此不需要系统输入/输出/错误流)。
There is a minor problem with this though, in that when the Java program exits, it doesn't really quit until all the launched programs are exited.
但这有一个小问题,即当 Java 程序退出时,它不会真正退出,直到所有启动的程序都退出。
I would greatly prefer it if the launched programs were completely independent of the JVM which launched them.
如果启动的程序完全独立于启动它们的 JVM,我会非常喜欢它。
The target operating system is multiple, with Windows, Linux and Mac being the minimum, but any GUI system with a JVM is really what is desired (hence the user configurability of the actual command lines).
目标操作系统有多种,Windows、Linux 和 Mac 是最少的,但任何带有 JVM 的 GUI 系统都是真正需要的(因此用户可配置实际命令行)。
Does anyone know how to make the launched program execute completely independently of the JVM?
有谁知道如何使启动的程序完全独立于 JVM 执行?
Edit in response to a comment
编辑回复评论
The launch code is as follows. The code may launch an editor positioned at a specific line and column, or it may launch an archive viewer. Quoted values in the configured command line are treated as ECMA-262 encoded, and are decoded and the quotes stripped to form the desired exec parameter.
启动代码如下。代码可能会启动位于特定行和列的编辑器,也可能会启动存档查看器。配置的命令行中的引用值被视为 ECMA-262 编码,并被解码并去除引号以形成所需的 exec 参数。
The launch occurs on the EDT.
发射发生在 EDT 上。
static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable {
String frs[][]={
{ "$FILE$" ,fil.getAbsolutePath().replace('\','/') },
{ "$LINE$" ,(lin>0 ? Integer.toString(lin) : "") },
{ "$COLUMN$",(col>0 ? Integer.toString(col) : "") },
};
String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values)
cmd=TextUtil.replace(cmd,frs,true,"$$","$");
arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true));
for(int xa=0; xa<arr.length; xa++) {
if(TextUtil.isQuoted(arr[xa],true)) {
arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa]));
}
}
log.println("Launching: "+cmd);
Runtime.getRuntime().exec(arr);
return null;
}
This appears to be happening only when the program is launched from my IDE. I am closing this question since the problem exists only in my development environment; it is not a problem in production. From the test program in one of the answers, and further testing I have conducted I am satisfied that it is not a problem that will be seen by any user of the program on any platform.
这似乎只有在从我的 IDE 启动程序时才会发生。我正在关闭这个问题,因为该问题仅存在于我的开发环境中;这在生产中不是问题。从其中一个答案中的测试程序以及我进行的进一步测试来看,我很满意这不是任何平台上该程序的任何用户都会看到的问题。
采纳答案by monceaux
It may help if you post a test section of minimal code needed to reproduce the problem. I tested the following code on Windows and a Linux system.
如果您发布重现问题所需的最少代码的测试部分,这可能会有所帮助。我在 Windows 和 Linux 系统上测试了以下代码。
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec(args[0]);
}
}
And tested with the following on Linux:
并在 Linux 上进行了以下测试:
java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh
where test.sh looks like:
其中 test.sh 看起来像:
#!/bin/bash
ping -i 20 localhost
as well as this on Linux:
以及Linux上的这个:
java -jar JustForTesting.jar gedit
And tested this on Windows:
并在 Windows 上对此进行了测试:
java -jar JustForTesting.jar notepad.exe
All of these launched their intended programs, but the Java application had no problems exiting. I have the following versions of Sun's JVM as reported by java -version
:
所有这些都启动了他们想要的程序,但 Java 应用程序退出没有问题。据报道,我有以下 Sun JVM 版本java -version
:
- Windows: 1.6.0_13-b03
- Linux: 1.6.0_10-b33
- 视窗:1.6.0_13-b03
- Linux:1.6.0_10-b33
I have not had a chance to test on my Mac yet. Perhaps there is some interaction occuring with other code in your project that may not be clear. You may want to try this test app and see what the results are.
我还没有机会在我的 Mac 上进行测试。也许在您的项目中与其他代码发生了一些可能不清楚的交互。您可能想试试这个测试应用程序,看看结果如何。
回答by Charlie Martin
回答by Ludwig Weinzierl
There is a parent child relation between your processes and you have to break that. For Windows you can try:
您的进程之间存在父子关系,您必须打破这种关系。对于 Windows,您可以尝试:
Runtime.getRuntime().exec("cmd /c start editor.exe");
For Linux the process seem to run detached anyway, no nohup necessary.
I tried it with gvim
, midori
and acroread
.
对于 Linux,该进程似乎无论如何都可以独立运行,不需要 nohup。我用gvim
,midori
和尝试过acroread
。
import java.io.IOException;
public class Exec {
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("/usr/bin/acroread");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Finished");
}
}
I think it is not possible to to it with Runtime.execin a platform independent way.
我认为以独立于平台的方式使用Runtime.exec是不可能的。
for POSIX-Compatible system:
对于 POSIX 兼容系统:
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();
回答by Matthew Flaschen
I suspect this would require a actual process fork. Basically, the C equivalent of what you want is:
我怀疑这需要一个实际的流程分支。基本上,你想要的 C 等价物是:
pid_t id = fork();
if(id == 0)
system(command_line);
The problem is you can't do a fork() in pure Java. What I would do is:
问题是你不能在纯 Java 中执行 fork() 。我会做的是:
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
Runtime.getRuntime().exec(command);
}
catch(IOException e)
{
// Handle error.
e.printStackTrace();
}
}
});
t.start();
That way the JVM still won't exit, but no GUI and only a limited memory footprint will remain.
这样 JVM 仍然不会退出,但不会有 GUI,只会保留有限的内存占用。
回答by Darren Greaves
One way I can think of is to use Runtime.addShutdownHook to register a thread that kills off all the processes (you'd need to retain the process objects somewhere of course).
我能想到的一种方法是使用 Runtime.addShutdownHook 注册一个终止所有进程的线程(当然,您需要将进程对象保留在某处)。
The shutdown hook is only called when the JVM exits so it should work fine.
关闭挂钩仅在 JVM 退出时调用,因此它应该可以正常工作。
A little bit of a hack but effective.
有点黑客但有效。
回答by Manvendra Gupta
Though this question is closed, I have some observations that may help other people facing similar issue.
虽然这个问题已经结束,但我有一些观察可以帮助其他面临类似问题的人。
When you use Runtime.getRuntime().exec() and then you ignore the java.lang.Process handle you get back (like in the code from original poster), there is a chance that the launched process may hang.
当您使用 Runtime.getRuntime().exec() 然后忽略返回的 java.lang.Process 句柄时(如原始海报中的代码),启动的进程可能会挂起。
I have faced this issue in Windows environment and traced the problem to the stdout and stderr streams. If the launched application is writing to these streams, and the buffer for these stream fills up then the launched application may appear to hang when it tries to write to the streams. The solutions are :
我在 Windows 环境中遇到过这个问题,并将问题追溯到 stdout 和 stderr 流。如果启动的应用程序正在写入这些流,并且这些流的缓冲区已满,则启动的应用程序在尝试写入流时可能会挂起。解决方案是:
- Capture the Process handle and empty out the streams continually - but if you want to terminate the java application right after launching the process then this is not a feasible solution
- Execute the process call as 'cmd /c <>' (this is only for Windows environment).
- Suffix the process command and redirect the stdout and stderr streams to nul using 'command > nul 2>&1'
- 捕获进程句柄并不断清空流 - 但是如果您想在启动进程后立即终止 java 应用程序,那么这不是一个可行的解决方案
- 以 'cmd /c <>' 执行进程调用(这仅适用于 Windows 环境)。
- 使用' command > nul 2>&1'为进程命令添加后缀并将stdout和stderr流重定向到nul
回答by Mroj
I tried everything mentioned here but without success. Main parent Java process can't quit until the quit of subthread even with cmd /c start and redirecting streams tu nul.
我尝试了这里提到的所有内容,但没有成功。即使使用 cmd /c start 和重定向流 tu nul,主父 Java 进程也无法退出,直到子线程退出。
Only one reliable solution for me is this:
对我来说只有一个可靠的解决方案是这样的:
try {
Runtime.getRuntime().exec("psexec -i cmd /c start cmd.cmd");
}
catch (Exception e) {
// handle it
}
I know that this is not clear, but this small utility from SysInternals is very helpful and proven. Hereis the link.
我知道这还不清楚,但是来自 SysInternals 的这个小实用程序非常有用且经过验证。这是链接。