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

Но я ничего не получаю в качестве вывода, но когда я сам запускаю эту команду, она работает нормально.

user541597
источник

Ответы:

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было бы хорошим выбором для использования.

Senthil
источник
4
@AlbertChen pwd && lsне просто выполняет один файл, когда вы делаете это в оболочке, он выполняет /bin/pwdи /bin/lsисполняемые файлы, и. Если вы хотите делать что-то подобное в Java, вам нужно сделать что-то вроде {"/bin/bash","-c", "pwd && ls"}. У вас, вероятно, больше нет вопроса, но другие люди могут, поэтому я подумал, что могу ответить на него.
735Tesla
3
Я думаю, что чтение двух потоков должно происходить одновременно, потому что если, как и в вашем случае, вывод stdStream заполнит буфер, вы не сможете прочитать поток ошибок ..
Li3ro
3
Li3ro частично прав. Программа , которую вы слушаете имеет ограниченный буфер для stdoutи stderrвывода. Если вы не слушаете их одновременно, один из них заполнится, а вы читаете другой. Программа, которую вы слушаете, затем заблокирует попытку записи в заполненный буфер, в то время как на другом конце ваша программа заблокирует попытку чтения из буфера, который никогда не вернется EOF. Вы должны читать из обоих потоков одновременно.
Гили
1
@Gili Тогда почему Li3ro "частично" прав? Разве Li3ro не совсем прав? В этом случае я не понимаю, почему неправильный ответ висит здесь с 2011 года и почему у него более 200 голосов ... Это сбивает с толку.
Андрей Тюкин
2
@AndreyTyukin Вы правы. Все текущие ответы уязвимы для тупиков. Я призываю их понизить рейтинг, чтобы другие ответы приобрели видимость. Я разместил новый ответ для вашего обзора: stackoverflow.com/a/57949752/14731 . Надеюсь, я понял это правильно ...
Гили
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;
}

Я знаю, что этот вопрос старый, но я публикую этот ответ, потому что я думаю, что это может быть быстрее.

735Tesla
источник
4
Спасибо за хороший ответ. Почему «\ A» является разделителем?
Готфрид
1
Я не совсем помню, какова была моя логика, когда я первоначально написал это. Я использовал это решение некоторое время, но я думаю, что это потому, что \Aв регулярном выражении означает начало строки, и мне пришлось избежать косой черты.
735Tesla
5
«\ A» - символ колокольчика. «^» - начало строки в регулярном выражении, а «$» - конец строки в регулярном выражении. Это персонаж, которого вы не ожидаете увидеть. В соответствии с документацией по Java в качестве разделителя по умолчанию используется пробел, поэтому выполнение этого, вероятно, приведет к полному результату выполнения команды.
Хэнк Шульц
11

Помимо использования, ProcessBuilderкак предложено Senthil, обязательно прочитайте и выполните все рекомендации, когда Runtime.exec () не будет .

Эндрю Томпсон
источник
Этот фрагмент, похоже, не использует стандартный поток ошибок (как рекомендуется в связанной статье). Это также не использует, ProcessBuilderкак сейчас рекомендуется дважды. Используя a ProcessBuilder, можно объединить потоки вывода и ошибок, чтобы упростить их использование одновременно.
Эндрю Томпсон
11

Если вы уже используете Apache commons-io на classpath, вы можете использовать:

Process p = new ProcessBuilder("cat", "/etc/something").start();
String stderr = IOUtils.toString(p.getErrorStream(), Charset.defaultCharset());
String stdout = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());
whirlwin
источник
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);
    }
Jackkobec
источник
7

@Senthil и @Arend answer ( https://stackoverflow.com/a/5711150/2268559 ) упомянуты ProcessBuilder. Вот пример использования 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);
nidalpres
источник
6

На момент написания этой статьи все остальные ответы, содержащие код, могут привести к взаимоблокировке.

Процессы имеют ограниченный буфер для stdoutи stderrвывода. Если вы не слушаете их одновременно, один из них заполнится, пока вы пытаетесь читать другой. Например, вы можете ожидать чтения, 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)который будет перенаправлять stderrв stdoutпоток. Это позволяет читать один поток без необходимости чередовать stdoutи stderr. Если вы хотите реализовать это вручную, вам придется использовать потоки в двух разных потоках, чтобы никогда не блокировать их.

Гили
источник
Ух ты! Я не ожидал, что вы ответите на комментарий так быстро, не говоря уже о том, чтобы ответить на этот вечно старый вопрос! :) Сейчас я рассматриваю возможность создания награды. Посмотрим на ваш ответ позже. Спасибо!
Андрей Тюкин
1

Если вы пишете на Kotlin, вы можете использовать:

val firstProcess = ProcessBuilder("echo","hello world").start()
val firstError = firstProcess.errorStream.readBytes().decodeToString()
val firstResult = firstProcess.inputStream.readBytes().decodeToString()
разработчик Android
источник
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;
       }
PYK
источник
-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.

брахмананда кар
источник