Java、これは、、一部のアクション)で記述されたプログラムに取り組んでおり、ユーザー設定コマンドを使用して外部プログラムを起動します。現在、Runtime.exec()
を使用し、Process
参照を保持しません(起動したプログラムはテキストエディターまたはアーカイブユーティリティであるため、システムのin/out/errストリームは不要です) 。
ただし、これには小さな問題があります。Javaプログラムが終了すると、起動されたすべてのプログラムが終了するまで実際には終了しません。
起動されたプログラムがそれらを起動したJVMから完全に独立している場合、私はそれを非常に好むでしょう。
ターゲットオペレーティングシステムは複数あり、Windows、Linux、およびMacが最小ですが、JVMを備えたGUIシステムは実際に必要なものです(したがって、実際のコマンドラインのユーザー設定が可能です)。
起動したプログラムをJVMから完全に独立して実行する方法を知っている人はいますか?
コメントに応じて編集
起動コードは次のとおりです。コードは、特定の行と列に配置されたエディターを起動するか、アーカイブビューアーを起動します。構成されたコマンドラインの引用符で囲まれた値は、ECMA-262でエンコードされたものとして扱われ、デコードされ、引用符が取り除かれて目的のexecパラメーターが形成されます。
起動は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;
}
これは、プログラムがIDEから起動されたときにのみ発生するようです。この問題は開発環境にのみ存在するため、この質問を終了します。 本番環境では問題ありません。回答の1つにあるテストプログラムと、私が実施したさらなるテストから、どのプラットフォームのプログラムのユーザーにも見られる問題ではないことに満足しています。
問題を再現するために必要な最小限のコードのテストセクションを投稿すると役立ちます。 WindowsおよびLinuxシステムで次のコードをテストしました。
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec(args[0]);
}
}
また、Linuxで以下を使用してテストしました。
Java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh
test.shは次のようになります。
#!/bin/bash
ping -i 20 localhost
linuxの場合:
Java -jar JustForTesting.jar gedit
Windowsでこれをテストしました:
Java -jar JustForTesting.jar notepad.exe
これらはすべて意図したプログラムを起動しましたが、Javaアプリケーションの終了に問題はありませんでした。Java -version
:
私はまだ私のMacでテストする機会がありませんでした。おそらく、プロジェクト内の他のコードと相互作用が発生していることがありますが、これは明確ではない場合があります。このテストアプリを試して、結果を確認することをお勧めします。
プロセス間に親子関係があり、それを破る必要があります。 Windowsの場合:
Runtime.getRuntime().exec("cmd /c start editor.exe");
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");
}
}
プラットフォームに依存しない方法で Runtime.exec を使用して実行することは不可能だと思います。
pOSIX互換システムの場合:
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();
この質問は閉じられていますが、同様の問題に直面している他の人々を助けるかもしれないいくつかの観察があります。
Runtime.getRuntime()。exec()を使用し、戻ったJava.lang.Processハンドルを無視すると(元のポスターのコードのように)、起動したプロセスがハングする可能性があります。
Windows環境でこの問題に直面し、stdoutおよびstderrストリームに問題を追跡しました。起動したアプリケーションがこれらのストリームに書き込みを行っており、これらのストリームのバッファがいっぱいになった場合、起動したアプリケーションはストリームへの書き込みを試みたときにハングしたように見えることがあります。ソリューションは次のとおりです。
バックグラウンドでプログラムを起動し、親から分離する必要があります。 Nohup(1) を検討します。
これには実際のプロセスフォークが必要だと思います。基本的に、あなたが望むものに相当するCは:
pid_t id = fork();
if(id == 0)
system(command_line);
問題は、pure 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();
このようにすると、JVMは終了しませんが、GUIはなく、限られたメモリフットプリントのみが残ります。
ここで述べたすべてを試しましたが、成功しませんでした。メイン親Javaプロセスは、cmd/c startおよびストリームtu nulをリダイレクトしても、サブスレッドの終了まで終了できません。
私にとって唯一の信頼できるソリューションはこれです:
try {
Runtime.getRuntime().exec("psexec -i cmd /c start cmd.cmd");
}
catch (Exception e) {
// handle it
}
これは明確ではないことは知っていますが、SysInternalsのこの小さなユーティリティは非常に役立ち、証明されています。 ここ はリンクです。