Java Runtime.getRuntime():コマンドラインプログラムの実行から出力を取得する


155

ランタイムを使用して、Javaプログラムからコマンドプロンプトコマンドを実行しています。ただし、コマンドが返す出力を取得する方法はわかりません。

これが私のコードです:

Runtime rt = Runtime.getRuntime();

String[] commands = {"system.exe", "-send" , argument};

Process proc = rt.exec(commands);

やってみた System.out.println(proc);が何も返ってきませんでした。そのコマンドを実行すると、セミコロンで区切られた2つの数値が返されます。これを変数に入れて出力するにはどうすればよいですか?

これが私が今使っているコードです:

String[] commands = {"system.exe", "-get t"};

Process proc = rt.exec(commands);

InputStream stdIn = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdIn);
BufferedReader br = new BufferedReader(isr);

String line = null;
System.out.println("<OUTPUT>");

while ((line = br.readLine()) != null)
     System.out.println(line);

System.out.println("</OUTPUT>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);

しかし、出力として何も得ていませんが、自分でそのコマンドを実行すると、正常に動作します。

回答:


244

ここに行く方法があります:

Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe", "-get t"};
Process proc = rt.exec(commands);

BufferedReader stdInput = new BufferedReader(new 
     InputStreamReader(proc.getInputStream()));

BufferedReader stdError = new BufferedReader(new 
     InputStreamReader(proc.getErrorStream()));

// Read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
    System.out.println(s);
}

// Read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
    System.out.println(s);
}

詳細については、Javadoc読んでここにProcessBuilder使用することをお勧めします。


4
@AlbertChen pwd && lsは単一のファイルを実行するだけではなく、シェルで実行する/bin/pwdと、/bin/ls実行可能ファイルと実行可能ファイルの両方を実行します。java内でそのようなことをしたい場合は、のようなことをする必要があります{"/bin/bash","-c", "pwd && ls"}。おそらくもう質問はありませんが、他の人が質問するかもしれないので、私は答えようと思いました。
735テスラ2015年

3
私はあなたのケースのように、stdStreamの出力はバッファを埋めるだろう、場合、あなたはエラーストリームを読むことができるように文句を言わないので、二つの流れが同時に起きている必要があります読んだと思う...
Li3ro

3
Li3roは部分的に正しいです。聴いているプログラムにはstdoutstderr出力および出力用のバッファが限られています。それらを同時に聞いていない場合は、片方を読んでいる間に片方がいっぱいになります。受信しているプログラムは、いっぱいになったバッファへの書き込みをブロックし、もう一方の端では、決して返されないバッファからの読み取りをブロックしますEOF。両方のストリームから同時に読み取る必要あります。
ギリ

1
@Giliでは、なぜLi3roは「部分的に」正しいのでしょうか。Li3roは完全にそして完全に正しいのではないですか?この場合、なぜ2011年以来ここで間違った答えが出回っているのか、なぜ200以上の賛成票があるのか​​理解できません...混乱しています。
Andrey Tyukin

2
@AndreyTyukinあなたは正しいです。現在のすべての回答はデッドロックに対して脆弱です。私は、他の回答が可視性を獲得できるように、反対投票することをお勧めします。私はあなたのレビューのために新しい回答を投稿しました:stackoverflow.com/a/57949752/14731。うまくいけば、私はこれを正しく
Gili

68

より速い方法はこれです:

public static String execCmd(String cmd) throws java.io.IOException {
    java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";
}

これは基本的に、これを凝縮したバージョンです。

public static String execCmd(String cmd) throws java.io.IOException {
    Process proc = Runtime.getRuntime().exec(cmd);
    java.io.InputStream is = proc.getInputStream();
    java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
    String val = "";
    if (s.hasNext()) {
        val = s.next();
    }
    else {
        val = "";
    }
    return val;
}

私はこの質問が古いことを知っていますが、私はこれがより速いかもしれないと思うので、この回答を投稿しています。


4
いい答えをありがとう。なぜ「\\ A」が区切り文字なのですか?
ゴットフリート、

1
私が最初にこれを書いたときの私の論理が何であったか私は完全に覚えていません。私はしばらくの間このソリューションを使用していますが\A、正規表現では文字列の始まりを意味し、スラッシュをエスケープする必要があったためだと思います。
735テスラ2014年

5
「\ A」はベル文字です。「^」は正規表現の文字列の始まりであり、「$」は正規表現の文字列の終わりです。これはあなたが見ることを期待しないであろうキャラクターです。Javaのドキュメントによると、デフォルトの区切り文字は空白なので、これを行うとコマンドの完全な結果が出力されます。
ハンクシュルツ

11

ProcessBuilder推奨されるSenthilの使用に加えて、Runtime.exec()が推奨しないすべての推奨事項を読んで実装してください。


そのスニペットは標準のエラーストリームを消費していないようです(リンクされた記事で推奨されています)。またProcessBuilder、現在推奨されているように2回は使用していません。を使用するProcessBuilderと、出力とエラーストリームをマージして、両方を同時に使いやすくすることができます。
Andrew Thompson

11

クラスパスで使用可能なApache commons-ioがすでに使用されている場合は、以下を使用できます。

Process p = new ProcessBuilder("cat", "/etc/something").start();
String stderr = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
String stdout = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());

7

また、コマンド出力を取得するためにストリームを使用できます。

public static void main(String[] args) throws IOException {

        Runtime runtime = Runtime.getRuntime();
        String[] commands  = {"free", "-h"};
        Process process = runtime.exec(commands);

        BufferedReader lineReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        lineReader.lines().forEach(System.out::println);

        BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        errorReader.lines().forEach(System.out::println);
    }

7

@Senthilおよび@Arendの回答(https://stackoverflow.com/a/5711150/2268559)が言及されましたProcessBuilderProcessBuilderコマンドの環境変数と作業フォルダーを指定して使用する例を次に示します。

    ProcessBuilder pb = new ProcessBuilder("ls", "-a", "-l");

    Map<String, String> env = pb.environment();
    // If you want clean environment, call env.clear() first
    //env.clear();
    env.put("VAR1", "myValue");
    env.remove("OTHERVAR");
    env.put("VAR2", env.get("VAR1") + "suffix");

    File workingFolder = new File("/home/user");
    pb.directory(workingFolder);

    Process proc = pb.start();

    BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));

    BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

    // Read the output from the command:
    System.out.println("Here is the standard output of the command:\n");
    String s = null;
    while ((s = stdInput.readLine()) != null)
        System.out.println(s);

    // Read any errors from the attempted command:
    System.out.println("Here is the standard error of the command (if any):\n");
    while ((s = stdError.readLine()) != null)
        System.out.println(s);

6

この記事の執筆時点では、コードを含む他のすべての回答がデッドロックになる可能性があります。

プロセスには、出力用のバッファーが限られstdoutていstderrます。それらを同時に聞かないと、片方がもう片方を読み込もうとしている間にいっぱいになります。たとえばstdout、プロセスがへの書き込みを待機している間、読み取りを待機している可能性がありますstderrstdoutバッファーが空のため、バッファーから読み取ることができませんstderr。バッファーがいっぱいのため、プロセスはバッファーに書き込むことができません。あなたはそれぞれ永遠にお互いを待っています。

デッドロックのリスクなしにプロセスの出力を読み取るための可能な方法を次に示します。

public final class Processes
{
    private static final String NEWLINE = System.getProperty("line.separator");

    /**
     * @param command the command to run
     * @return the output of the command
     * @throws IOException if an I/O error occurs
     */
    public static String run(String... command) throws IOException
    {
        ProcessBuilder pb = new ProcessBuilder(command).redirectErrorStream(true);
        Process process = pb.start();
        StringBuilder result = new StringBuilder(80);
        try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())))
        {
            while (true)
            {
                String line = in.readLine();
                if (line == null)
                    break;
                result.append(line).append(NEWLINE);
            }
        }
        return result.toString();
    }

    /**
     * Prevent construction.
     */
    private Processes()
    {
    }
}

重要なのは、ストリームにProcessBuilder.redirectErrorStream(true)リダイレクトstderrすることstdoutです。これにより、stdoutとを交互に切り替えることなく、単一のストリームを読み取ることができますstderr。これを手動で実装する場合は、ブロックしないように、2つの異なるスレッドでストリームを使用する必要があります。


ああすごい!あなたがコメントにすぐに返信するとは思わなかった。この永遠に古い質問に答えることは言うまでもない!:)私は現在、賞金を始めることを検討しています。後で回答を見ていきます。ありがとう!
Andrey Tyukin

1

Kotlinで書く場合は、以下を使用できます。

val firstProcess = ProcessBuilder("echo","hello world").start()
val firstError = firstProcess.errorStream.readBytes().decodeToString()
val firstResult = firstProcess.inputStream.readBytes().decodeToString()

0

前の回答から適応:

public static String execCmdSync(String cmd, CmdExecResult callback) throws java.io.IOException, InterruptedException {
    RLog.i(TAG, "Running command:", cmd);

    Runtime rt = Runtime.getRuntime();
    Process proc = rt.exec(cmd);

    //String[] commands = {"system.exe", "-get t"};

    BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
    BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

    StringBuffer stdOut = new StringBuffer();
    StringBuffer errOut = new StringBuffer();

    // Read the output from the command:
    System.out.println("Here is the standard output of the command:\n");
    String s = null;
    while ((s = stdInput.readLine()) != null) {
        System.out.println(s);
        stdOut.append(s);
    }

    // Read any errors from the attempted command:
    System.out.println("Here is the standard error of the command (if any):\n");
    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
        errOut.append(s);
    }

    if (callback == null) {
        return stdInput.toString();
    }

    int exitVal = proc.waitFor();
    callback.onComplete(exitVal == 0, exitVal, errOut.toString(), stdOut.toString(), cmd);

    return stdInput.toString();
}

public interface CmdExecResult{
    void onComplete(boolean success, int exitVal, String error, String output, String originalCmd);
}

0

このページの他のスニペットとほとんど同じですが、関数の上に整理するだけです。

String str=shell_exec("ls -l");

クラス関数:

public String shell_exec(String cmd)
       {
       String o=null;
       try
         {
         Process p=Runtime.getRuntime().exec(cmd);
         BufferedReader b=new BufferedReader(new InputStreamReader(p.getInputStream()));
         String r;
         while((r=b.readLine())!=null)o+=r;
         }catch(Exception e){o="error";}
       return o;
       }

-1

InputStreamランタイムのを読んでみてください:

Runtime rt = Runtime.getRuntime();
String[] commands = {"system.exe", "-send", argument};
Process proc = rt.exec(commands);
BufferedReader br = new BufferedReader(
    new InputStreamReader(proc.getInputStream()));
String line;
while ((line = br.readLine()) != null)
    System.out.println(line);

proc.getErrorStream()プロセスがエラー出力を出力している場合は、エラーストリーム()を読み取る必要がある場合もあります。を使用すると、エラーストリームを入力ストリームにリダイレクトできますProcessBuilder

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.