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

Але я нічого не отримую як результат, але коли я сам запускаю цю команду, це працює чудово.

Відповіді:


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Тесла

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

Я знаю, що це питання давнє, але я публікую цю відповідь, тому що я думаю, що це може бути швидше.


4
Дякую за гарну відповідь. Чому роздільник "\\ A"?
Готфрід

1
Я не пам'ятаю повністю, якою була моя логіка, коли я це писав. Я використовую це рішення деякий час, але я думаю, що це було тому, що \Aв регулярному виразі означає початок рядка, і мені довелося уникнути косої риски.
735Тесла

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

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

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

Якщо ви пишете на Котлін, ви можете використовувати:

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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.