I am using Java ProcessStream to write input commands to powershell on my local machine. Now, the problem is that along with the output, I am seeing input commands as well. How can I restrict input command from being shown on the output.
Below is the code to reproduce the same:
public class Example {
public static boolean isAlive(Process o) {
try {
p.exitValue();
return false;
} catch (Exception e) {return true;}
}
public stati void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder("powershell");
builder.redirectErrorStream(true);
Process process = builder.start();
InputStream out = process.getInputStream();
OutputStream in = process.getOutputStream();
String bufferString = "echo hello \n";
byte[] buffer = bufferString.getBytes();
int n = buffer.length;
in.write(buffer, 0, n);
in.flush();
while(isAlive(process)) {
int no = out.available();
if (no >0) {
int m = out.read(buffer, 0, Math.min(no, buffer.length));
System.out.println(new String(buffer, 0, m));
}
}
}
}
OUTPUT:
PS C://> echo hello
hello
I need only "hello" in output.
The following works for me (but the java program never terminates and I have to kill it).
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class Example {
public static boolean isAlive(Process p) {
try {
p.exitValue();
return false;
}
catch (Exception e) {
return true;
}
}
public static void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder("powershell", "-NoLogo");
builder.redirectErrorStream(true);
Process process = builder.start();
InputStream is = process.getInputStream();
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader out = new BufferedReader(isr)) {
OutputStream in = process.getOutputStream();
String bufferString = "echo hello" + System.lineSeparator();
byte[] buffer = bufferString.getBytes();
in.write(buffer);
in.flush();
while (isAlive(process)) {
String line = out.readLine();
if (!line.startsWith("PS")) {
System.out.println(line);
}
}
}
}
}
The output produced by the above code is simply:
hello
#talex mentioned the following in his comment to your question:
it is impossible to get rid of PS C://> part
This is not true since you can customize the PowerShell prompt but you wrote that you don't want to see the echo hello in the output and that is not possible and therefore, as #talex mentioned:
you have to filter your input stream yourself
Not displaying echo hello means not displaying the entered command. Imagine that you open a PowerShell window and don't see the command you entered but after you hit ENTER you want to see the output. So I don't think that there is a way to not display echo hello and therefore, in the above code, I don't print lines that start with the PowerShell prompt.
You can find details about launching PowerShell in about_PowerShell_exe
Related
Need to run sqlcmd to control local-connections to the database.
When i try to read the OutputStream to get the sql "prompt", the read() method simply never returns.
tried running the process with ProcessBuilder and Runtime.getRuntime().exec()
also tried running under "cmd /c"
when running sqlcmd from the command line, i get a prompt like "1>". expected to see that in the OutputStream ...
this is my code - i simply call start with "sqlcmd"
code example:
package org.example;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(start("sqlcmd"));
System.out.println(send("select * from spt_monitor\ngo\n"));
}
public static Process process = null;
public static String start(String command) throws Exception {
if(process != null && process.isAlive()) {
throw new Exception("another session already exists");
} else {
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
process = builder.start();
return "";//response();
}
}
public static String send(String command) throws Exception {
if(process == null || !process.isAlive()) {
throw new Exception("no local session exists");
} else {
process.getOutputStream().write(command.getBytes());
process.getOutputStream().flush();
return response();
}
}
private static String response() throws IOException {
StringBuilder sb = new StringBuilder();
char[] array = new char[1024];
System.out.println("reading output...");
try (InputStreamReader isr = new InputStreamReader(process.getInputStream())) {
int len;
do {
len = isr.read(array);
if(len > 0)
sb.append(String.valueOf(array));
} while (len > 0);
}
return sb.toString();
}
}
This hopefully will give a clearer explanation as to why your code does not finish as you expect. You have started sub-process sqlcmd, and sqlcmd outputs a prompt, receives your command(s), processes each command and outputs another prompt.
The sqlcmd process is waiting for more commands but you have not sent more after send(). At this point though your response() call is stuck in a blocking loop trying to read the character AFTER the last prompt. This means that response() will never exit normally - even though it has processed some of the commands and appended to a StringBuilder.
To see this more clearly, fix a small bug in response() with incorrect size of the array in String.valueOf() and add a System.out to tell you what has been read so far by response():
private static String response() throws IOException {
StringBuilder sb = new StringBuilder();
char[] array = new char[1024];
System.out.println("reading output...");
try (InputStreamReader isr = new InputStreamReader(process.getInputStream())) {
int len;
do {
len = isr.read(array);
if(len > 0) {
String r = String.valueOf(array, 0, len);
System.out.println("response() has read a value: "+r);
sb.append(r);
}
} while (len > 0);
}
return sb.toString();
}
Re-run and you should see that isr.read(array) HAS worked and should print out the results of select * from spt_monitor but will block on next or following isr.read(array) call when the prompt has been printed - and response() never exits because no more commands are received to process.
There is no more STDOUT to process unless you send sqlcmd an exit command OR terminate the STDIN which causes the sqlcmd process to end - both of which would mean that response() could exit normally.
You should be able to make response() work by terminating the STDIN by adding process.getOutputStream().close() at the end of send() or after calling send(). However that means that sqlcmd will exit.
I have a simple program that runs the echo command with ProcessBuilder, and the program works perfectly fine on my Linux machine, but it throws an IOException when running on Windows.
This is a simplified version of my program. It takes echo and hello as arguments for ProcessBuilder, and then saves the output into a string and prints the output. In Linux, the output is hello, and in Windows an IOException is caught.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestPB {
public static void main(String[] args) throws InterruptedException {
ProcessBuilder pb = new ProcessBuilder("echo", "hello");
try {
Process process = pb.start();
BufferedReader readProcessOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
String output = "";
String line = "";
while ( (line = readProcessOutput.readLine()) != null) {
output += line;
output += System.getProperty("line.separator");
}
process.waitFor();
if(output.length() > 0) {
System.out.println(output.substring(0, output.length() -1));
} else {
System.out.println("No result");
}
} catch (IOException io) {
System.out.println("IOException thrown");
}
}
}
Does anyone know why this is not working in Windows?
echo is not a program on Windows, it's an internal shell command*, so you need to invoke the command-line interpreter:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "echo", "hello");
*) Reference: Wikipedia
I am trying to use the cat command from within a java program to merge two files into one. The line of code which contains the cat command takes two files file1 and file2 and writes to a third file called combinedfile. However, what I observe is the instead of creating this file (combinedfile) and writing to it, my program merely displays the output on the terminal.
How can I make sure that indeed the two files are copied to a third file.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellCommand
{
public static void main(String[] args)
{
ExecuteShellCommand obj = new ExecuteShellCommand();
String command = "cat file1 file2 > combinedfile";
String output = obj.executeCommand(command);
System.out.println(output);
}
private String executeCommand(String command)
{
StringBuffer output = new StringBuffer();
Process p;
try
{
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null)
{
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
EDIT:
I tried out with the ProcessBuilder as suggested, but I get this error.
Code
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
import java.util.*;
public class ExecuteShellCommand
{
public static void main(String[] args)
{
try
{
ProcessBuilder builder = new ProcessBuilder("cat", "/home/PepperBoy/Desktop/file1.txt","/home/PepperBoy/Desktop/file2.txt");
File combinedFile = new File("/home/PepperBoy/Desktop/file3.txt");
builder.redirectOutput(combinedFile);
builder.redirectError(combinedFile);
Process p = builder.start();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
Error
ExecuteShellCommand.java:14: cannot find symbol
symbol : method redirectOutput(java.io.File)
location: class java.lang.ProcessBuilder
builder.redirectOutput(combinedFile);
I found a related question. To summarize the useful things from a couple answers found there, file redirection requires a shell, but exec doesn't have a shell context. Luckily, you can execute processes with redirection using ProcessBuilder. For your case, that would look something like:
public static void main(String[] args)
{
try{
ProcessBuilder builder = new ProcessBuilder("cat", "file1","file2");
File combinedFile = new File("combinedFile");
builder.redirectOutput(combinedFile);
builder.redirectError(combinedFile);
Process p = builder.start();
} catch(IOException e){
//handle exception...
}
}
Note: You may receive an error when calling redirectError or redirectOutput stating that the symbol cannot be found. This will occur if you are compiling against a version of Java before 1.7, since 1.7 is when these methods were introduced. If it is possible to upgrade your Java, doing so will eliminate this error.
If it is not possible to upgrade Java, the following code will work:
public static void main(String[] args)
{
try{
ProcessBuilder builder = new ProcessBuilder("cat", "file1","file2");
File combinedFile = new File("combinedFile");
Process p = builder.start();
InputStream isFromCat = p.getInputStream();
OutputStream osCombinedFile = new FileOutputStream(combinedFile);
byte[] buffer = new byte[1024];
int read = 0;
while((read = isFromCat.read(buffer)) != -1) {
osCombinedFile.write(buffer, 0, read);
}
} catch(IOException e){
//handle exception...
}
}
It is also probably worthwhile to note that making a system call to cat is not the optimal way to combine files from Java. I have been assuming this is a toy case to represent a more complex use case for you. If all you really want to do is combine two files, you should write your code to avoid system calls and just append the files by reading both in as input streams and then writing them out to the result file. If you're having trouble figuring that out, those details definitely belong in another question.
I'm currently working on a project where a client receives shell/console commands from a server, and must execute them.
How do I get Java to run these commands from within either a shell or a command prompt? I'm hoping to be able to disregard the platform type - and not have to specify shell or command prompt - but if I can't, then that's okay.
I must be able to send a sequence of related commands, not just one command. This means that the shell/prompt cannot exit or close between commands.
My current code, as follows, allows for the execution of a sequence of programs, but these commands must somehow be piped into a shell/command prompt, from which the output must be read.
ArrayList<String> comDat = new ArrayList<>();
while(true) {
String input = con.recv();
System.out.println("> " + input);
if(!input.equals("EOF")) comDat.add(input); else {
String[] s = new String[comDat.size()];
for(int i = 0; i < comDat.size(); i++) s[i] = comDat.get(i);
System.out.println("---Command sequence executing---");
Process p = Runtime.getRuntime().exec(s);
p.waitFor();
System.out.println("---ErrorStream output---"); String line = "";
BufferedReader errStream = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while((line = errStream.readLine()) != null) System.out.println("< " + line);
System.out.println("\n---OutputStream output---"); line = "";
BufferedReader outStream = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((line = errStream.readLine()) != null) System.out.println("< " + line);
}
Thread.sleep(200);
}
Thanks for the help!
The basic premise revoles around the fact the dir isn't an external command but is function of cmd.
I would avoid BufferedReaders when reading the output of a process as not all processes use new lines when sending output (such as progress indicators), instead you should read char for char (IMHO).
You should us ProcessBuilder instead of Runtime#exec. It provides better management and allows you to redirect the error stream into the input stream, making it easier to read the input.
import java.io.IOException;
import java.io.InputStream;
public class TestProcessBuilder {
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "dir");
pb.redirectError();
Process p = pb.start();
InputStreamConsumer isc = new InputStreamConsumer(p.getInputStream());
isc.start();
int exitCode = p.waitFor();
isc.join();
System.out.println("Process terminated with " + exitCode);
} catch (IOException | InterruptedException exp) {
exp.printStackTrace();
}
}
public static class InputStreamConsumer extends Thread {
private InputStream is;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
#Override
public void run() {
try {
int value = -1;
while ((value = is.read()) != -1) {
System.out.print((char)value);
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
}
I would like to execute foo.bat from within a Groovy program and have the resulting process' output redirected to stdout. Either a Java or Groovy code example would be fine.
foo.bat can take several minutes to run and generates a lot of output, so I would like to see the output as soon as it is generated, rather than having to wait until the process has completed before seeing all the output at once.
It is simple to redirect all your stream to standard output using inheritIO() method. This will print the output to the stdout of the process from which you are running this command.
ProcessBuilder pb = new ProcessBuilder("command", "argument");
pb.directory(new File(<directory from where you want to run the command>));
pb.inheritIO();
Process p = pb.start();
p.waitFor();
There exist other methods too, like as mentioned below. These individual methods will help redirect only required stream.
pb.redirectInput(Redirect.INHERIT)
pb.redirectOutput(Redirect.INHERIT)
pb.redirectError(Redirect.INHERIT)
This uses a class which reads all output the executed program generates and displays it in it's own stdout.
class StreamGobbler extends Thread {
InputStream is;
// reads everything from is until empty.
StreamGobbler(InputStream is) {
this.is = is;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
System.out.println(line);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("javac");
//output both stdout and stderr data from proc to stdout of this process
StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream());
StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream());
errorGobbler.start();
outputGobbler.start();
proc.waitFor();
If you're looking to do this with more Groovy and less java, this will print each line as it happens:
def cmd = "./longRunningProcess"
def process = cmd.execute()
process.in.eachLine { line -> println line }
Alternatively, if you want to see both stdout and stderr
def cmd = "./longRunningProcess"
def process = cmd.execute()
process.waitForProcessOutput( System.out, System.err )
Here's something a little simpler if you're just trying to grab the output of a simple command. You'll need to use threads like jitter does if you want to process in parallel or if your command takes stdin or generates stderr.
Use a buffered copy (like this) if you're getting lots of output.
import java.io.*;
public class test {
static void copy(InputStream in, OutputStream out) throws IOException {
while (true) {
int c = in.read();
if (c == -1) break;
out.write((char)c);
}
}
public static void main(String[] args) throws IOException, InterruptedException {
String cmd = "echo foo";
Process p = Runtime.getRuntime().exec(cmd);
copy(p.getInputStream(), System.out);
p.waitFor();
}
}
The following Groovy code will execute foo.bat and send the output to stdout:
println "foo.bat".execute().text
Asynchronous way to achieve it.
void inputStreamToOutputStream(final InputStream inputStream, final OutputStream out) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
int d;
while ((d = inputStream.read()) != -1) {
out.write(d);
}
} catch (IOException ex) {
//TODO make a callback on exception.
}
}
});
t.setDaemon(true);
t.start();
}
{
Process p = ...;
inputStreamToOutputStream(p.getErrorStream(), System.out);
inputStreamToOutputStream(p.getInputStream(), System.out);
}
VerboseProcess from jcabi-log can help you:
String output = new VerboseProcess(new ProcessBuilder("foo.bat")).stdout();