Vaibhav's Blog Space

Not as easy as we thought - PowerShell from Java Runtime

Friday Feb 22, 2008

This week, one of my office colleagues,Vijay asked me " Can we run command of PowerShell from Java Application ?" Microsoft is coming up with a new shell called PowerShell, commands are very similar to that of our Unix Shell. Yes, this problem looks quite simple, I guess Java Runtime class will do that as it do for cmd commands. And for that we wrote one simple code like this :

import java.io.*;
class OneMore {
public static void main(String[] args)throws IOException
{
          Runtime runtime = Runtime.getRuntime();
           String cmds[] = {"ls"};
           Process proc = runtime.exec(cmds);
           InputStream inputstream = proc.getInputStream();
           InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
           BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
           String line;
           while ((line = bufferedreader.readLine()) != null) {
               System.out.println(line);
           }
}
}

So, this code will print the output of ls on console. But no we need to execute powershell command. So it is required to pass PowerShell in the String cmds[]. Ok, I don't have PowerShell on my machine but let me try to run the same with command.exe(cmd.exe) in place of powershell.exe. So, I have changed one line in the code:

String cmds[] = {"cmd", "ls"};

But then this is not right as there should be right option with cmd like /c or /k. So further it changed into :

String cmds[] = {"cmd", "\c", "ls"};

Code is running fine and we are done with job. But he want a file in place of command "ls", not a problem pass a file "file.bat" in place of ls. file.bat contains ls :). So, finally code goes something like:

Runtime runtime = Runtime.getRuntime();
           String cmds[] = {"cmd", "\c", "file.bat"};
           Process proc = runtime.exec(cmds);
           InputStream inputstream = proc.getInputStream();
           InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
           BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
           String line;
           while ((line = bufferedreader.readLine()) != null) {
               System.out.println(line);
           }


Even if we want to check the process is exiting correctly or not, we can add some more lines checking for proc.waitFor() or proc.ExitValue().

But, this piece of code is not running fine for PowerShell. WHY ? No clue(till today, trying to find out why). Unfortunately the program go for a hang. And till now the conclusion is: its either a problem that we are not able to find the right substitute of \c in PowerShell, though the document is saying "&" will do the same Or maybe PowerShell is stopping us in writing streams(some security issue).

A bad workaround is working fine.

Process proc = runtime.exec("powershell & filename" > out.txt)

We are able to pass the output of PowerShell process into a file out.txt, now reading the file out.txt and print the output on console. But it can create problem if out.txt will go large enough. Can you give me the answer of my WHY ? Why this code is not running for PowerShell. Is there any bug in PowerShell(according to me, yes) ?

[6] Comments
Like this post? del.icio.us | furl | slashdot | technorati | digg
Comments:

This works with cmd.exe, because cmd.exe outputs text for you to consume with BufferedReader. Powershell emits objects, not text, so you program has to work with that instead. Furthermore, cmd.exe works on the "stdout stderr" system, where Powershell has different pipes for output,error,verbose,etc. Redirecting powershell's output to a file works, because your BufferedReader understands text files. It should be able to work with "powershell -command 'ls c:\temp'", but it's been awhile since I programmed Java and don't remember the Runtime class too well.

Posted by James Pogran on February 22, 2008 at 08:52 PM IST #

Thanks James. This sounds little new to me. I will definitely give a try with this and let you know.

Posted by Vaibhav on February 23, 2008 at 08:07 PM IST #

I had the same problem and the problems seems to be that powershell first reads all input from it's input stream before continuing. The simple solution is to call proc.getOutputStream().close(); after you create the process.

Posted by Christoph Hohmann on March 05, 2008 at 07:58 PM IST #

Oh this is a good workaround. I thought it is some security issue with PowerShell.

Thanks for the information.

Posted by Vaibhav on March 05, 2008 at 08:29 PM IST #

Thank you! Thank you! Thank you for this solution!
I need to administer Exchange Server 2007 mailboxes using a JSP and Java servlet in Tomcat. Since Exchange 2007 administration can only be done through PowerShell cmdlets, I have no option except to execute a PowerShell script from Java. The servlet was "hanging" as described above until I added the .close() code.

Posted by Mark Wefer on August 13, 2008 at 08:01 PM IST #

O yes, even I thanks for Christoph for the comment. Vijay's work was also got done because of the comment. Thanks again from all of us :)

Posted by Vaibhav on August 13, 2008 at 08:04 PM IST #

Post a Comment:
  • HTML Syntax: NOT allowed