ProcessBuilder working in Linux but not Windows - java

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

Related

Restrict Powershell from printing input commands to output stream

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

how running a windows command (cmd) in a folder specified from java

Good day, I thank who can help me in advance.
I need to run a command to read a file that contains the input data of a program and it will return a file with the output data.
Criteria:
I must locate myself in the folder where is the executable file of the software, then I write the following command:
swmm5 prueba1.inp prueba1.rpt prueba1.out
If I run it in the cmd, it generates output files:
swmm5 is the executable file
test1.inp is the file with the input data
test1.rpt is an output data file (this file is created when I run the command)
test1.out is the file with the output data (this file is created when I run the command)
The code I'm trying to use is this, although it generate no errors, it does not run the above command line successfully, because it does not generate the output files. Also I tried with the String that is in comments // (comando)
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class OptimizarSWMM {
public static void main(String[] args) {
Runtime cmd = Runtime.getRuntime();
Process proceso;
String[] directorio = new String[4];
directorio[0] = "cmd.exe";
directorio[1] = "/C";
directorio[2] = "C:\\Users\\milton\\Desktop\\EPA_SWMM_5.1";
directorio[3] = "swmm5 prueba1.inp prueba1.rpt prueba1.out";
//String comando = "cmd /C C:\\Users\\milton\\Desktop\\EPA_SWMM_5.1 swmm5 prueba1.inp prueba1.rpt prueba1.out";
try {
proceso = cmd.exec(directorio);
//proceso = cmd.exec(comando);
} catch (IOException ex) {
Logger.getLogger(OptimizarSWMM.class.getName()).log(Level.SEVERE,null, ex);
}
}
}
i could solve it thus:
"cmd.exe", "/c", "cd \"C:\\Users\\milton\\Desktop\\EPA_SWMM_5.1\" && swmm5 prueba1.inp prueba1.rpt prueba1.out"
this is the code:
public static void main(String[] args) throws IOException {
ProcessBuilder builder = new ProcessBuilder(
"cmd.exe", "/c", "cd \"C:\\Users\\milton\\Desktop\\EPA_SWMM_5.1\" && swmm5 prueba1.inp prueba1.rpt prueba1.out");
builder.redirectErrorStream(true);
Process p = builder.start();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while (true) {
line = r.readLine();
if (line == null) { break; }
System.out.println(line);
}
}

Can't get output from Runtime.exec()

I have written a code to execute a command on shell through Java:
String filename="/home/abhijeet/sample.txt";
Process contigcount_p;
String command_to_count="grep \">\" "+filename+" | wc -l";
System.out.println("command for counting contigs "+command_to_count);
contigcount_p=Runtime.getRuntime().exec(command_to_count);
contigcount_p.wait();
As pipe symbols were being used so I was not able to execute command successfully.As per my last question's discussion i have wrapped my variables in shell:
Runtime.getRuntime().exec(new String[]{"sh", "-c", "grep \">\" "+filename+" | wc -l"});
Which worked for me as it does executes command on shell , but still when i try to read its output using buffered reader :
BufferedReader reader =
new BufferedReader(new InputStreamReader(contigcount_p.getInputStream()));
String line=" ";
while((line=reader.readLine())!=null)
{
output.append(line+"\n");
}
It returns a null value ,I have found a temporary solution for it as i have discussed on previous question: link, but i would like to use right way of doing it by reading it's output using BufferedReader.
When I used your command line of {"sh", "-c", "grep \">\" "+filename+" | wc -l"} it kept overriding my file
I had to change it so that the quotes were double quoted, {"sh", "-c", "grep \"\">\"\" "+filename+" | wc -l"}
So, using this as the contents of my test file...
>
>
>
Not a new line >
And using this code...
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class TestProcess {
public static void main(String[] args) {
String filename = "test.tx";
String test = "grep \"\">\"\" "+filename+" | wc -l";
System.out.println(test);
try {
ProcessBuilder pb = new ProcessBuilder("sh", "-c", test);
pb.redirectError();
Process p = pb.start();
new Thread(new Consumer(p.getInputStream())).start();
int ec = p.waitFor();
System.out.println("ec: " + ec);
} catch (IOException | InterruptedException exp) {
exp.printStackTrace();
}
}
public static class Consumer implements Runnable {
private InputStream is;
public Consumer(InputStream is) {
this.is = is;
}
#Override
public void run() {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))){
String value = null;
while ((value = reader.readLine()) != null) {
System.out.println(value);
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
}
I was able to produce this output...
grep "">"" test.tx | wc -l
4
ec: 0
Generally, when dealing with external processes, it's usually easier to use a ProcessBuilder, it has some nice options, including redirecting the error/stdout and setting the execution context directory...

How to supply sudo with root password from Java?

I'm trying to write a small Java app that will overwrite my /etc/resolv.conf file (I'm on Ubuntu 12.04). To do so, I need to supply my root password:
myUser#myMachine:~$ sudo vim /etc/resolv.conf
[sudo] password for myUser: *****
So the process for doing this has three steps:
Type sudo vim /etc/resolv.conf at terminal
Terminal asks me to type my root password
I enter the password and press [Enter]
From everything I've researched, I could use the following for performing step #1 above:
try {
String installTrickledCmd = "sudo vim /etc/resolv.conf";
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec(installTrickledCmd);
}
catch(Throwable throwable) {
throw new RuntimeException(throwable);
}
But when this executes, the shell will want to prompt my Java process for the password. I'm not sure how to wait for this (step #2 above) and then to supply my password back to the shell (step #3 above). Thanks in advance.
Have you tried with -S ?
$echo mypassword | sudo -S vim /etc/resolv.conf
From man:
The -S (stdin) option causes sudo to read the password from the standard input
instead of the terminal device. The password must be followed by a newline
character.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class Test1 {
public static void main(String[] args) throws Exception {
String[] cmd = {"sudo","-S", "ls"};
System.out.println(runSudoCommand(cmd));
}
private static int runSudoCommand(String[] command) throws Exception {
Runtime runtime =Runtime.getRuntime();
Process process = runtime.exec(command);
OutputStream os = process.getOutputStream();
os.write("harekrishna\n".getBytes());
os.flush();
os.close();
process.waitFor();
String output = readFile(process.getInputStream());
if (output != null && !output.isEmpty()) {
System.out.println(output);
}
String error = readFile(process.getErrorStream());
if (error != null && !error.isEmpty()) {
System.out.println(error);
}
return process.exitValue();
}
private static String readFile(InputStream inputStream) throws Exception {
if (inputStream == null) {
return "";
}
StringBuilder sb = new StringBuilder();
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = bufferedReader.readLine();
while (line != null) {
sb.append(line);
line = bufferedReader.readLine();
}
return sb.toString();
} finally {
if (bufferedReader != null) {
bufferedReader.close();
}
}
}
}
Inspired from Viggiano answer.

How to invoke a Linux shell command from Java

I am trying to execute some Linux commands from Java using redirection (>&) and pipes (|). How can Java invoke csh or bash commands?
I tried to use this:
Process p = Runtime.getRuntime().exec("shell command");
But it's not compatible with redirections or pipes.
exec does not execute a command in your shell
try
Process p = Runtime.getRuntime().exec(new String[]{"csh","-c","cat /home/narek/pk.txt"});
instead.
EDIT::
I don't have csh on my system so I used bash instead. The following worked for me
Process p = Runtime.getRuntime().exec(new String[]{"bash","-c","ls /home/XXX"});
Use ProcessBuilder to separate commands and arguments instead of spaces. This should work regardless of shell used:
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(final String[] args) throws IOException, InterruptedException {
//Build command
List<String> commands = new ArrayList<String>();
commands.add("/bin/cat");
//Add arguments
commands.add("/home/narek/pk.txt");
System.out.println(commands);
//Run macro on target
ProcessBuilder pb = new ProcessBuilder(commands);
pb.directory(new File("/home/narek"));
pb.redirectErrorStream(true);
Process process = pb.start();
//Read output
StringBuilder out = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null, previous = null;
while ((line = br.readLine()) != null)
if (!line.equals(previous)) {
previous = line;
out.append(line).append('\n');
System.out.println(line);
}
//Check result
if (process.waitFor() == 0) {
System.out.println("Success!");
System.exit(0);
}
//Abnormal termination: Log command parameters and output and throw ExecutionException
System.err.println(commands);
System.err.println(out.toString());
System.exit(1);
}
}
Building on #Tim's example to make a self-contained method:
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
public class Shell {
/** Returns null if it failed for some reason.
*/
public static ArrayList<String> command(final String cmdline,
final String directory) {
try {
Process process =
new ProcessBuilder(new String[] {"bash", "-c", cmdline})
.redirectErrorStream(true)
.directory(new File(directory))
.start();
ArrayList<String> output = new ArrayList<String>();
BufferedReader br = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = null;
while ( (line = br.readLine()) != null )
output.add(line);
//There should really be a timeout here.
if (0 != process.waitFor())
return null;
return output;
} catch (Exception e) {
//Warning: doing this is no good in high quality applications.
//Instead, present appropriate error messages to the user.
//But it's perfectly fine for prototyping.
return null;
}
}
public static void main(String[] args) {
test("which bash");
test("find . -type f -printf '%T#\\\\t%p\\\\n' "
+ "| sort -n | cut -f 2- | "
+ "sed -e 's/ /\\\\\\\\ /g' | xargs ls -halt");
}
static void test(String cmdline) {
ArrayList<String> output = command(cmdline, ".");
if (null == output)
System.out.println("\n\n\t\tCOMMAND FAILED: " + cmdline);
else
for (String line : output)
System.out.println(line);
}
}
(The test example is a command that lists all files in a directory and its subdirectories, recursively, in chronological order.)
By the way, if somebody can tell me why I need four and eight backslashes there, instead of two and four, I can learn something. There is one more level of unescaping happening than what I am counting.
Edit: Just tried this same code on Linux, and there it turns out that I need half as many backslashes in the test command! (That is: the expected number of two and four.) Now it's no longer just weird, it's a portability problem.

Categories