58

私は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つにあるテストプログラムと、私が実施したさらなるテストから、どのプラットフォームのプログラムのどのユーザーにも見られる問題ではないことに満足しています。

4

7 に答える 7

29

プロセス間には親子関係があり、それを破る必要があります。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();
于 2009-05-31T07:44:25.630 に答える
25

同様の問題に直面している他の人々を助けるかもしれないいくつかの観察があります。

Runtime.getRuntime().exec() を使用し、返された java.lang.Process ハンドルを無視すると (元のポスターのコードのように)、起動されたプロセスがハングする可能性があります。

Windows 環境でこの問題に直面し、問題を stdout および stderr ストリームに追跡しました。起動されたアプリケーションがこれらのストリームに書き込みを行っているときに、これらのストリームのバッファがいっぱいになると、起動されたアプリケーションがストリームに書き込もうとするとハングしたように見えることがあります。解決策は次のとおりです。

  1. プロセス ハンドルをキャプチャし、ストリームを継続的に空にします。ただし、プロセスの起動直後に Java アプリケーションを終了する場合、これは実現可能なソリューションではありません。
  2. としてプロセス呼び出しを実行しますcmd /c <<process>>(これは Windows 環境のみです)。
  3. process コマンドにサフィックスを付け、' command > nul 2>&1 'を使用して stdout および stderr ストリームを nul にリダイレクトします。
于 2012-06-13T12:36:31.267 に答える
22

問題を再現するために必要な最小限のコードのテスト セクションを投稿すると、役立つ場合があります。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 アプリケーションは問題なく終了しました。によって報告された次のバージョンの Sun の JVM がありますjava -version

  • Windows: 1.6.0_13-b03
  • Linux: 1.6.0_10-b33

Mac でテストする機会はまだありません。プロジェクト内の他のコードとの相互作用が発生している可能性がありますが、これは明確ではない可能性があります。このテスト アプリを試して、結果を確認してください。

于 2009-05-31T08:47:09.030 に答える
1

プログラムをバックグラウンドで起動し、親から分離します。nohup(1)を検討します。

于 2009-05-31T07:21:33.273 に答える
0

これには実際のプロセスフォークが必要になると思います。基本的に、Cに相当するものは次のとおりです。

pid_t id = fork();
if(id == 0)
  system(command_line);

問題は、純粋な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はなく、限られたメモリフットプリントしか残りません。

于 2009-05-31T07:47:18.760 に答える
-2

私が考えることができる 1 つの方法は、Runtime.addShutdownHook を使用して、すべてのプロセスを強制終了するスレッドを登録することです (もちろん、プロセス オブジェクトをどこかに保持する必要があります)。

シャットダウン フックは、JVM が終了するときにのみ呼び出されるため、正常に動作するはずです。

少しハックですが効果的です。

于 2009-05-31T07:59:45.707 に答える