2

So I am writing a program in Java that needs to be able to run on Windows or Linux and needs to be able to make requests to the command line such as dir or ls. It then needs to get the resultant output from these commands and AFTERWARDS prompt for another command. Here is my code:

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

I should mention that I'm currently testing on a Windows 7 machine and that anything after a # on a line is considered as a comment.

When I run this, say using dir, the result comes back just fine, but try running it with ls (which gives a message saying that it's not a recognized command in Windows) and the error stream may be printed before the next prompt (??) (which is desirable), after the next prompt, or partially before and partially after the prompt. Is there a way to consistently get the error message printing BEFORE the prompt?

Here is an example of what's happening now:

??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

My other question regards when I try running a command such as date. In the DOS window, this gives a 2-line response and waits for more input, but in Java, I only get the first line of the response (although with dir I seem to get all the lines), but then after that first date line, the program just hangs... does anyone know why this might be happening?

Thanks a lot!

3 Answers 3

1

When you run the ls, there is an error, and the error would be shown in both stdout and in error stream,

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

Because of above code block, errors are always printed after std out, so you could switch the loops to get desirable output.

When in the date prompt, it asks for the new date, where the prompt will wait for a user input. Where your program is not giving any, so it waits there and you see it as java program got stuck.

Edit : Try this, as this will use separate threads,

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);
    }
    }
Sign up to request clarification or add additional context in comments.

4 Comments

That is probably because there is no errors in the dir command, I think you should try to debug and see what is going on there
I tried to simply switch the order of the while loops. The result was that when I tried to run dir, no output was generated and the program just hung. This didn't fix the fact the prompt might still print anywhere relative to the output stream either. I thought combining the 2 blocks might help (see following post - no room), but this had the same problems as switching the blocks' order...
I have added code with threads to the answer, I think that should work.
Thank you. This certainly clears up the problem with the ls result String printing at random locations relative to the prompt... in fact, in the original code, if the while loops in question are set to append to a String and then that String printed after both loops complete using System.out.println(theCollectedString); this clears up the problem as well.
1

To get the output always in correct order make sure to flush() both output and error streams.

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

Regarding the date command, it expects user input so you can do following:

process.getOutputStream().close(); // after runtime.exec

Comments

0

You must use separate threads to read output from the process when it is running, if it fills up the buffer between parent/child processes it will basically hang. This was a huge problem on Windows XP -- the buffer was small and if you didn't start the other thread right away the process was as good as dead. This is why your program hung when you swapped the error/stdout readers as you mentioned in your comments.

I haven't tried Windows 7, so it might be better.

Its also handy to use the option in Java to merge the output and error streams if you don't really care about them being separate. That way you don't need to launch two threads.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.