Командная строка в Java (и многопоточность) - PullRequest
2 голосов
/ 06 февраля 2012

Итак, я пишу программу на Java, которая должна быть в состоянии работать в Windows или Linux и должна иметь возможность отправлять запросы в командную строку, такие как dir или ls. Затем он должен получить результирующий вывод из этих команд и запрос AFTERWARDS для другой команды. Вот мой код:

import java.io.*;
import java.util.*;

public class myShell
{
    public static void main(String[] args)
    {
        Runtime runtime = Runtime.getRuntime();
        Process process;
        Scanner keyboard = new Scanner(System.in);
        String userInput = "";
        String osName = System.getProperty("os.name" );
        BufferedReader brStdOut = null;
        String stdOut;
        BufferedReader brErrorStream = null;
        String errorStream;

        while(userInput.compareToIgnoreCase("exit") != 0)
        {
            System.out.println();
            System.out.print("??");

            userInput = keyboard.nextLine();

            int indexOfPound = userInput.indexOf('#');
            if(indexOfPound == 0)
            {
                userInput = "";
            }
            else if(indexOfPound > 0)
            {
                userInput = userInput.substring(0, indexOfPound);
            }
            userInput = userInput.trim();

            try
            {   
                if(osName.contains("Windows"))
                {
                    process = runtime.exec("cmd /c " + userInput);
                }
                else
                {
                    process = runtime.exec(userInput);
                }

                brStdOut = new BufferedReader(new InputStreamReader(
                           process.getInputStream()));
                brErrorStream = new BufferedReader(new InputStreamReader(
                                process.getErrorStream()));

                while((stdOut = brStdOut.readLine())  != null)
                {
                    System.out.println(stdOut);
                }
                while((errorStream = brErrorStream.readLine()) != null)
                {
                    System.err.println(errorStream);
                }

                process.waitFor();
            }
            catch(Exception e)
            {
                System.out.println("Error executing: " + userInput); 
                System.out.println(e.getMessage());
            }
            try
            {
                brStdOut.close();
            }
            catch(Exception e)
            {
            }
            try
            {
                brErrorStream.close();
            }
            catch(Exception e)
            {
            }
        }
        System.exit(0);
    }
}

Я должен упомянуть, что в настоящее время я тестирую на компьютере с Windows 7, и что-либо после # в строке считается комментарием.

Когда я запускаю это, скажем, используя dir, результат возвращается просто отлично, но попробуйте запустить его с помощью ls (который выдает сообщение о том, что это не распознанная команда в Windows), и поток ошибок может быть напечатан до следующей подсказка (??) (что желательно), после следующей подсказки или частично до и частично после подсказки. Есть ли способ последовательно получать сообщение об ошибке печати ДО приглашения?

Вот пример того, что происходит сейчас:

??dir
 Volume in drive C is Windows7_OS
 Volume Serial Number is SomeNumbers

 Directory of C:\Users\BlankedOut\Documents\Java\Eclipse\Workspace\Prog1

02/05/2012  03:48 PM    <DIR>          .
02/05/2012  03:48 PM    <DIR>          ..
02/05/2012  03:48 PM               301 .classpath
02/05/2012  03:48 PM               387 .project
02/05/2012  03:48 PM    <DIR>          .settings
02/05/2012  08:39 PM    <DIR>          bin
02/05/2012  08:39 PM    <DIR>          src
               2 File(s)            688 bytes
               5 Dir(s)  861,635,362,816 bytes free

??ls

??'ls' is not recognized as an internal or external command,
operable program or batch file.
ls
'ls' is not recognized as an internal or external command,
operable program or batch file.

??exit

Мой другой вопрос касается того, когда я пытаюсь запустить такую ​​команду, как дата. В окне DOS это дает двухстрочный ответ и ждет большего ввода, но в Java я получаю только первую строку ответа (хотя с помощью dir я, кажется, получаю все строки), но затем после этой первой даты линия, программа просто зависает ... кто-нибудь знает, почему это может происходить?

Большое спасибо!

Ответы [ 3 ]

1 голос
/ 06 февраля 2012

Чтобы вывод всегда был в правильном порядке, убедитесь, что flush () выводил и потоки ошибок.

System.err.flush();  //after writing to error stream and 
System.out.flush();  //after writing to output stream

Что касается команды date, она ожидает ввода данных пользователем, поэтому вы можете сделать следующее:

process.getOutputStream().close(); // after runtime.exec
1 голос
/ 06 февраля 2012

Когда вы запускаете ls, возникает ошибка, и ошибка будет отображаться как в stdout, так и в потоке ошибок,

                while((stdOut = brStdOut.readLine())  != null)
                {
                    System.out.println(stdOut);
                }
                while((errorStream = brErrorStream.readLine()) != null)
                {
                    System.err.println(errorStream);
                }

Из-за вышеуказанного блока кода ошибки всегда выводятся после std out, так что вы можете переключать циклы для получения желаемого результата.

Когда в приглашении даты, он запрашивает новую дату, где приглашение будет ждать ввода пользователя.Там, где ваша программа не дает ничего, поэтому она ждет там, и вы видите это как застрявшую java-программу.

Редактировать: Попробуйте, так как при этом будут использоваться отдельные потоки,

import java.lang.*;
import java.io.*;

public class StreamGobbler implements Runnable {
    String name;
    InputStream is;
    Thread thread;
    String output="";

    public StreamGobbler (String name, InputStream is) {
    this.name = name;
    this.is = is;
    }

    public void start () {
    thread = new Thread (this);
    thread.start ();
    }

    public void run () {
    try {
    InputStreamReader isr = new InputStreamReader (is);
    BufferedReader br = new BufferedReader (isr);

       while (true) {
          String s = br.readLine ();
          if (s == null) break;
          output += s;
       }
       is.close ();
       } catch (Exception ex) {
          System.out.println ("Problem reading stream " + name + "... :" + ex);
         ex.printStackTrace ();
       }
    }
}

    public class ProgA {
    public static void main(String[] args) throws Exception {
    Runtime rt = Runtime.getRuntime();
    Process p = rt.exec("execute command prompt");
    StreamGobbler s1 = new StreamGobbler ("stdin", p.getInputStream ());
    StreamGobbler s2 = new StreamGobbler ("stderr", p.getErrorStream ());
    s1.start ();
    s2.start ();
    p.waitFor();
    System.out.println(s2.output + s1.output);
    }
    }
0 голосов
/ 06 февраля 2012

Вы должны использовать отдельные потоки для чтения выходных данных процесса, когда он выполняется, если он заполняет буфер между родительскими / дочерними процессами, он в основном зависает.Это было огромной проблемой в Windows XP - буфер был маленьким, и если вы сразу не запустили другой поток, процесс оказался практически мертвым.Вот почему ваша программа зависала, когда вы меняли читателей ошибок / stdout, как вы упоминали в ваших комментариях.

Я не пробовал Windows 7, так что может быть лучше.

Также удобно использовать опцию в Java для объединения потоков вывода и ошибок, если вы действительно не заботитесь о том, чтобы они были разделены.Таким образом, вам не нужно запускать два потока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...