Получить вывод командной строки в строку в Java

Мне нужен метод Java, который будет читать вывод командной строки и сохранять его в строку для чтения в Java.

Это то, что у меня есть до сих пор, но работает неправильно.

public void testGetOutput() {
    System.out.println("\n\n****This is the testGetOutput Method!****");
    String s = null;
    String query = "dir " + this.desktop;
    try {
        Runtime runtime = Runtime.getRuntime();
        InputStream input = runtime.exec("cmd /c " + query).getInputStream();
        BufferedInputStream buffer = new BufferedInputStream(input);
        BufferedReader commandResult = new BufferedReader(new InputStreamReader(buffer));
        String line = "";
        try {
            while ((line = commandResult.readLine()) != null) {
                s += line + "\n";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(s);
    } catch (Exception e) {
        e.printStackTrace();
    }
}//end testGetOutput()

Я думаю, что проблема заключается в том, что я пытаюсь изменить запрос на команду, которая будет выполнять HandBrakeCLI.exe. Глядя на мою систему, когда программа работает (но, похоже, была приостановлена), она показывает мне, что HandBrakeCLI.exe работает в окне cmd, которое запускается в моей среде IDE. Все это имеет смысл, но HandBrakeCLI.exe не завершается, поэтому я предполагаю, что именно поэтому я не могу прочитать вывод в качестве ввода для моей программы.

Итак, после этого фона. Мой большой вопрос заключается в следующем: как заставить HandBrakeCLI.exe закрыться после завершения моего запроса, чтобы я мог получить его вывод? Просто для дополнительной информации: единственная разница между описанным выше методом и методом сканирования DVD, который у меня есть для HandBrakeCLI, заключается в том, что переменная запроса отличается. Как этот пример:

String query = "C:\Users\Kent\Desktop\HBCLI\HandBrakeCLI -t --scan -i "C:\Users\Kent\Desktop\General Conference DVDs\Sources\174th October 2004\DVD 1"; //this is actually a variable in the DVD object, but here's an example'

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

Вот исходная проблема (я не уверен, как повторно отправить вопрос):

Я искал везде и не могу понять это. Я не уверен, что то, что я нашел, даже имеет отношение к тому, что я хочу делать. У меня пока нет достаточного количества кода для этого, поэтому размещение кода здесь не очень поможет, и я думаю, что это должно быть довольно просто, поэтому я приведу здесь несколько скриншотов. Итак, вот моя задача:

  1. Отсканируйте папку, которая заполнена скопированными папками DVD (папки Video_TS с файлами VOB и т. д.), и сохраните имена этих папок в качестве названия DVD.

  2. Сканируйте каждую папку с помощью HandBrakeCLI и сохраняйте вывод в строку.

  3. Регулярно выражайте строку, чтобы идентифицировать каждый заголовок, главу и язык.

  4. Генерируйте запросы, чтобы вернуть их в HandBrakeCLI для массового кодирования каждого языка в каждой главе в каждом заголовке для каждого DVD (вы понимаете, почему я хочу автоматизировать это!)

  5. Сохраните эти запросы в файле *.bat

Единственная часть, в которой я не уверен, это шаг 2! Все остальное я могу сделать довольно легко. Я много читал о OutputStreams, но никак не могу понять, как это работает. Мне действительно просто нужно получить вывод в строку, которую я могу регулярно использовать, чтобы получить то, что мне нужно. Вот скриншоты того, что мне нужно ввести и что мне нужно убрать из вывода:

Вход в HandBrakeCLI:

введите здесь описание изображения

Вывод на сканирование:

введите здесь описание изображения


person kentcdodds    schedule 03.10.2011    source источник


Ответы (3)


Сначала вам нужен неблокирующий способ чтения из Standard.out и Standard.err

private class ProcessResultReader extends Thread
{
    final InputStream is;
    final String type;
    final StringBuilder sb;

    ProcessResultReader(@Nonnull final InputStream is, @Nonnull String type)
    {
        this.is = is;
        this.type = type;
        this.sb = new StringBuilder();
    }

    public void run()
    {
        try
        {
            final InputStreamReader isr = new InputStreamReader(is);
            final BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
            {
                this.sb.append(line).append("\n");
            }
        }
        catch (final IOException ioe)
        {
            System.err.println(ioe.getMessage());
            throw new RuntimeException(ioe);
        }
    }

    @Override
    public String toString()
    {
        return this.sb.toString();
    }
}

Затем вам нужно связать этот класс с соответствующими объектами InputStream и OutputStream.

    try
    {
        final Process p = Runtime.getRuntime().exec(String.format("cmd /c %s", query));
        final ProcessResultReader stderr = new ProcessResultReader(p.getErrorStream(), "STDERR");
        final ProcessResultReader stdout = new ProcessResultReader(p.getInputStream(), "STDOUT");
        stderr.start();
        stdout.start();
        final int exitValue = p.waitFor();
        if (exitValue == 0)
        {
            System.out.print(stdout.toString());
        }
        else
        {
            System.err.print(stderr.toString());
        }
    }
    catch (final IOException e)
    {
        throw new RuntimeException(e);
    }
    catch (final InterruptedException e)
    {
        throw new RuntimeException(e);
    }

Это в значительной степени шаблон, который я использую, когда мне нужно Runtime.exec() что-нибудь на Java.

Более продвинутым способом было бы использование FutureTask и Callable или хотя бы Runnable, а не прямое расширение Thread, что не является лучшей практикой.

ПРИМЕЧАНИЕ:

Аннотации @Nonnull находятся в библиотеке JSR305. Если вы используете Maven, и вы используете Maven, не так ли, просто добавьте эту зависимость в свой файл pom.xml.

<dependency>
  <groupId>com.google.code.findbugs</groupId>
  <artifactId>jsr305</artifactId>
  <version>1.3.9</version>
</dependency>
person Community    schedule 11.10.2011
comment
Джаррод, спасибо за быстрый ответ! Я постараюсь реализовать это прямо сейчас и дам вам знать, как это происходит! - person kentcdodds; 11.10.2011
comment
Идеальный! Я не совсем уверен, как это работает. Но я посмотрю немного, чтобы понять это. Но да, это сработало как шарм! Теперь мне просто нужно регулярно выражать все это! В любом случае, большое спасибо за вашу помощь! О, и я не уверен, что такое Maven. Моя IDE — Netbeans. Мне не нужно было добавлять «зависимость». Спасибо еще раз! - person kentcdodds; 11.10.2011

В этом примере полной программы на Java выполняется команда 'dir' (список каталогов) в командной строке, результат преобразуется в строку и выводится на консоль.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class X {
    public static void main(String[] args) {
        try{
            String command = "dir";
            String s = get_commandline_results(command);
            System.out.println(s);
        }
        catch(Exception e){
            e.printStackTrace();
        }
        System.out.println("done");
    }

    public static String get_commandline_results(String cmd)
        throws IOException, InterruptedException, IllegalCommandException{

        //Do not remove the authorizedCommand method.  Be prepared 
        //to lose your hard drive if you have not white-listed the 
        //commands that can run.
        if (!authorizedCommand(cmd)) 
            throw new IllegalCommandException();

        String result = "";
        final Process p = Runtime.getRuntime().
            exec(String.format("cmd /c %s", cmd));
        final ProcessResultReader stderr = new ProcessResultReader(
                p.getErrorStream(), "STDERR");
        final ProcessResultReader stdout = new ProcessResultReader(
                p.getInputStream(), "STDOUT");
        stderr.start();
        stdout.start();
        final int exitValue = p.waitFor();
        if (exitValue == 0){
            result = stdout.toString();
        }
        else{
            result = stderr.toString();
        }
        return result;
    }
    public static boolean authorizedCommand(String cmd){
        //Do not allow any command to be run except for the ones 
        //that we have pre-approved here.  This lessens the 
        //likelihood that fat fingers will wreck your computer.
        if (cmd.equals("dir"))
            return true;
        //add the commands you want to authorize here.

        return false;
    }
}

class ProcessResultReader extends Thread{
    final InputStream is;
    final String type;
    final StringBuilder sb;

    ProcessResultReader(final InputStream is, String type){
        this.is = is;
        this.type = type;
        this.sb = new StringBuilder();
    }

    public void run()
    {
        try{
            final InputStreamReader isr = new InputStreamReader(is);
            final BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine()) != null)
            {
                this.sb.append(line).append("\n");
            }
        }
        catch (final IOException ioe)
        {
            System.err.println(ioe.getMessage());
            throw new RuntimeException(ioe);
        }
    }
    @Override
    public String toString()
    {
        return this.sb.toString();
    }
}
class IllegalCommandException extends Exception{
    private static final long serialVersionUID = 1L;
    public IllegalCommandException(){   }
}

В Windows это результат, который я получаю

Directory of D:\projects\eric\eclipseworkspace\testing2
07/05/2012  01:06 PM    <DIR>          .
07/05/2012  01:06 PM    <DIR>          ..
06/05/2012  11:11 AM               301 .classpath
06/05/2012  11:11 AM               384 .project
06/05/2012  11:11 AM    <DIR>          .settings
07/05/2012  01:42 PM    <DIR>          bin
06/05/2012  11:11 AM    <DIR>          src
07/05/2012  01:06 PM             2,285 usernames.txt
               3 File(s)          2,970 bytes
               5 Dir(s)  45,884,035,072 bytes free

done
person Eric Leschinski    schedule 05.07.2012
comment
+1 за публикацию еще одного работоспособного решения для будущих поколений. - person kentcdodds; 05.07.2012

Предполагая, что вы используете Runtime.exec() для запуска внешнего процесса, дающего вам объект Process, для этого объекта есть три соответствующих метода: getOutputStream(), getInputStream() и getErrorStream().

Я думаю, что в этом легко запутаться - вы, вероятно, думаете, что хотите увидеть результат процесса, поэтому вам нужен "выходной поток". Но вам нужно помнить, что имена этих объектов определяются с точки зрения кода Java — OutputStream предназначен для Java для записи вывода, а InputStream — для Java для чтения ввода. Вывод вашего внешнего процесса, с точки зрения Java, является вводом.

Таким образом, вы должны использовать getInputStream() и вызывать соответствующие методы для возвращаемого им InputStream для чтения вывода. Вы также можете использовать getErrorStream(), чтобы убедиться, что вы ничего не пропустили, потому что это записывается в стандартную ошибку.

person Dave Costa    schedule 03.10.2011
comment
Благодарю за разъяснение! Это немного сбивало с толку. Проблема, с которой я столкнулся сейчас, заключается в том, что вместо того, чтобы печатать результаты запроса (как видно на моем втором снимке экрана), он печатает сам запрос. В коде слишком много контента, чтобы добавить его в комментарий, поэтому вот ссылка на документ Collabedit: collabedit.com/5r384. - person kentcdodds; 03.10.2011
comment
Одна проблема, я думаю, заключается в том, что exec() ожидает фактического исполняемого файла - пакетный файл - это не исполняемая программа, это последовательность команд, которые должны выполняться оболочкой Windows. См. stackoverflow.com/questions/615948/. - person Dave Costa; 03.10.2011
comment
Мы очень близко! Итак, теперь моя проблема в том, что мой буферизованный объект чтения равен нулю. Я не уверен, почему. Я обновил код в документе Collabedit. Вы заметите, что я поставил println вокруг commandResult, потому что именно здесь код останавливается, а commandResult.readLine() = null, когда он должен равняться первой строке ввода (или, по крайней мере, это то, что я ищу). Спасибо еще раз за помощь! collabedit.com/5r384 - person kentcdodds; 04.10.2011
comment
Ах, я думаю, проблема в использовании start во внешней команде, о которой я ничего не думал, когда отсылал вас к другому ответу. Это открывает отдельное командное окно, в котором будет записан вывод команды, поэтому он не переходит к стандартному выводу фактического процесса cmd. Попробуйте удалить start из команды, например. cmd /c path-to-batch-file.bat. - person Dave Costa; 04.10.2011
comment
Я не могу в это поверить. Это все еще не работает. commandResult.readLine() просто читает команду, а не вывод команды. (например, если бы команда была dir, то commandResult.readLine() вернула бы dir, а не вывод команды dir). Есть ли способ преобразовать вывод этой команды в строку? Вот обновленный код: collabedit.com/5r384 Еще раз спасибо за вашу помощь, Дэйв! Я знаю, что кто-то умнее меня мог бы сделать это! Надеюсь, я справлюсь с этим и смогу внести хороший вклад в stackoverflow :) - person kentcdodds; 05.10.2011
comment
Еще одна вещь, которая может быть полезна, заключается в том, что код останавливается в цикле while: while ((line = commandResult.readLine()) != null) {...} Он будет работать нормально (вроде как) в течение нескольких итераций (достаточно, чтобы напечатать все команды в файле *.bat), затем он остановится (даже при отладке, и он не выдает ошибок, он просто не будет двигаться дальше), и NetBeans сообщает мне, что он все еще работает. Не уверен, в чем проблема. - person kentcdodds; 05.10.2011
comment
Что ж, теперь он у меня есть, поэтому он даже не входит в цикл while, потому что 'commandResult.readLine()' действительно имеет значение null, поэтому он полностью пропускает цикл. Я обновил код... collabedit.com/5r384 - person kentcdodds; 05.10.2011