Java Runtime.getRuntime():从执行命令行程序获取输出


155

我正在使用运行时从Java程序运行命令提示符命令。但是,我不知道如何获得命令返回的输出。

这是我的代码:

Runtime rt = Runtime.getRuntime();

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

Process proc = rt.exec(commands);

我尝试做,System.out.println(proc);但是没有返回任何东西。该命令的执行应返回两个数字,以分号分隔。我怎样才能在变量中打印出来?

这是我现在使用的代码:

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);

但是我没有得到任何输出,但是当我自己运行该命令时,它可以正常工作。

Answers:


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"}。您可能不再有这个问题,但其他人可能会再说,我想我可以回答。
735Tesla 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”是分隔符?
Gottfried 2014年

1
我不完全记得我最初写这篇文章时的逻辑。我使用这种解决方案已有一段时间,但是我认为这是因为\A在正则表达式中表示字符串的开头,因此我不得不转义斜线。
735Tesla 2014年

5
“ \ A”是响铃字符。“ ^”是正则表达式中字符串的开头,“ $”是正则表达式中字符串的结尾。这是您不希望看到的字符。根据Java文档,默认的分隔符为空格,因此这样做可能会吐出命令的完整结果。
汉克·舒尔茨2015年

11

除了ProcessBuilder按照建议的方式使用Senthil之外,请务必阅读并实现When Runtime.exec()will not not的所有建议。


该代码段似乎并没有占用标准错误流(如链接文章中所建议)。它还不使用ProcessBuilder两次,因为现在建议这样做。使用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/2268559ProcessBuilder。这是ProcessBuilder为命令指定环境变量和工作文件夹的示例:

    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

在撰写本文时,所有其他包含代码的答案都可能导致死锁。

工艺具有用于限制缓冲器stdoutstderr输出。如果您不同时收听它们,那么当您尝试阅读另一种时,它们中的一种会变满。例如,stdout当进程正在等待写入时,您可能正在等待读取stderr。您无法从stdout缓冲区读取数据,因为它为空,并且进程无法写入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)它将重定向stderrstdout流中。这样一来,您无需在stdout和之间切换即可读取单个流stderr。如果要手动实现此功能,则必须在两个不同的线程中使用流,以确保永不阻塞。


哦,哇!我没想到您会这么快就评论发表评论,更不用说回答这个永恒的问题了!:)我正在考虑开始赏金。稍后将查看您的答案。谢谢!
安德烈·秋金

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.