How to run bash from java on windows - java

I want to execute Bash commands from within a Java program running on a Windows desktop. I installed Cygwin, and I have found the following code:
public class BashRunner {
private static final Logger log = Logger.getLogger(BashRunner.class.getName());
public static void run(String comand) throws IOException, InterruptedException {
Runtime run = Runtime.getRuntime();
String[] env = new String[] {"path=%PATH%;C:/cygwin/bin/"};
Process proc = run.exec(new String[]{"bash.exe", comand}, env);
proc.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
while (br.ready()) {
System.out.println(br.readLine());
}
}
}
The problem is that the process enters an infinite loop...
The BashRunner.run(String) method is called from the main method of my program like so:
BashRunner.run("ls -alt");

Related

Java ProcessBuilder execute multiple commands in sequence on same Java program

I am new to process building and I am trying to get a java program that will call another java program that expects inputs before printing. After looking through stackoverflow it seems I need a processbuilder for each command and for each process. I am unsure if the flowing process will be interacting with the java program launched? Currently the program doesn't throw any errors but I am not getting the final print. I have provided both programs.
import java.util.Scanner;
public class test {
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
sc.nextLine();
sc.nextLine();
System.out.println("you have done it");
}
}
public class test2 {
public static void main(String args[]){
Process process;
ProcessBuilder pb = new ProcessBuilder("java -jar Test.jar");
try {
process = pb.start();
process.waitFor();
ProcessBuilder pb1 = new ProcessBuilder("no");
Process process1 = pb1.start();
process1.waitFor();
ProcessBuilder pb2 = new ProcessBuilder("no");
Process process2;
process2 = pb2.start();
process2.waitFor();
System.out.println(process2.getInputStream());
}catch(Exception e){
}
}
}

Bash -c with Java Process API

I've been messing about with the java process API and ran into the following case:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
var command = "echo foo";
System.out.println(command);
run(command);
command = "bash -c 'echo bar'";
System.out.println(command);
run(command);
}
public static void run(String command) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
var br = new BufferedReader(new InputStreamReader(p.getInputStream()));
br.lines().forEach(System.out::println);
}
}
Why does bar not print? I've also tried other commands like systemctl poweroff and mkdir, but none seem to execute. I've also experimented with nohup, which works on its own but not with bash -c.
You call exec(String), which in turn calls exec(String,String,File), which in turn uses a StringTokenizer to chop the command line passed as a single string into an argument list, and then calls exec(String[],String,File).
However, that tokenizer just chops at spaces (it doesn't know that it's working with a command line or what shell would be involved). That means you end up with these tokens as the command: bash, -c, 'echo, foo' --- note the single quotes; Runtime.exec does not involve a shell to handle quotes (or variable substitution or such).
bash then complains about the 'echo, but you don't see that cause you only print the child process' stdout, but not stderr. Add code like this to run:
br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
System.out.println("stderr:");
br.lines().forEach(System.out::println);
This gets me:
stderr:
bar': -c: line 1: unexpected EOF while looking for matching `''
bar': -c: line 2: syntax error: unexpected end of file
Now if you remove the single quotes from the call, you just get a single empty line because bash -c expects only one argument to run, which here is the echo, which prints a new line.
To fix this, you need to call the exec version that takes a String[] directly so that you control what is one argument:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
run("echo", "foo");
run("bash", "-c", "echo bar");
}
public static void run(String... command) throws IOException, InterruptedException {
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
var br = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("stdout:");
br.lines().forEach(System.out::println);
br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
System.out.println("stderr:");
br.lines().forEach(System.out::println);
}
}
Personally, I'd use something higher level like the standard ProcessBuilder class or the exec library from Apache Commons instead, as they have better support for building complicated commands to execute:
import java.io.IOException;
import org.apache.commons.exec.*;
public class ExecDemo {
// Build and execute a command with Apache Commons Exec
private static void demoApacheExec()
throws IOException, ExecuteException {
var cmd = new CommandLine("sh");
cmd.addArgument("-c");
cmd.addArgument("echo test 1", false);
System.out.println("Command: " + cmd);
var executor = new DefaultExecutor();
executor.execute(cmd);
}
// Build and execute a command with ProcessBuilder
private static void demoProcessBuilder()
throws IOException, InterruptedException {
// Can also take a List<String> of arguments
var pb = new ProcessBuilder("sh", "-c", "echo test 2").inheritIO();
System.out.println("Command: " + pb.command());
pb.start().waitFor();
}
public static void main(String[] args) {
try {
demoApacheExec();
demoProcessBuilder();
} catch (Exception e) {
System.err.println("Error executing program: " + e);
System.exit(1);
}
}
}
The important thing is making each argument its own separate thing instead of relying on trying to parse a single string like a shell would (A.C.E has a CommandLine.parse() function, but even it has issues with quoting; addArgument likes to put actual quotes around arguments with spaces in them when it doesn't actually need to unless you tell it not to.).

Execute command displaying console window and also get handle of the process

I am trying to run a command from Java that will start a process that runs for several minutes. I need to just trigger the command and get the process handle and continue with other operations in a loop. At regular intervals, I will have to monitor that the process is still active.
I also need the console window to display to show the output of the process for the user.
Currently, I have tried methods from both Runtime and ProcessBuilder classes to run my command but neither of them has helped me achieve my objective.
Sample code:
//Changing the directory and running Maven exec: java command on the POM file in that directory.
String cmd = "cd C:/Test & mvn exec:java";
String finalCmd = "cmd /c \""+ cmd +"\"";
Process process = Runtime.getRuntime().exec(finalCmd);
Thread.sleep(10);
boolean alive = process.isAlive();
The value of variable alive is True, but I don't see the process got started. When the program execution is complete, only then the process starts and I am not sure why that happens.
Also to display the console window, I found from google that I need to use the below command:
String finalCmd = "cmd /c start cmd.exe /c \"" + cmd + "\"";
However, with this, the process starts immediately but I do not get the process handle as I find the alive variable shows false.
Does someone know how this objective can be achieved? I am ok if it's not possible to do both at the same time but at least I need to get the process execution to start and get the handle to monitor the process state later in my code.
Couple of things that are happening incorrectly here:
We need to pass our command as string tokens to the exec() command
We need to wait for the process to exit with process.waitFor() instead of sleeping, this will block the current thread so if you don't want that you need to execute this in another thread or use an ExecutorService.
Advisable to check the output value of waitFor() to see if our command executed properly (value of 0) or not (any other value,
typically a positive 1 in case of unsuccessful execution)
Optionally (to see the output) we need to redirect the standard OUT and ERR somewhere, say print it to console(), though you could put it to a file some GUI window etc.
So at a minimum the following code should work:
Process process = Runtime.getRuntime().exec(new String[] {"cmd", "/c", "cd", "C:\\dev", "&&", "dir"});
int outputVal = process.waitFor();
boolean alive = process.isAlive();
System.out.format("alive %s, outputVal: %d\n",alive, outputVal);
Further suggestions:
use ProcessBuilder instead of runTime.exec(), it allows more control
and is the recommended way since JDK 1.5
read the inputStream
So the code will look some thing like this:
List<String> cmdList = Arrays.asList("cmd", "/c", "cd", "C:\\dev", "&&", "dir");
ProcessBuilder pb = new ProcessBuilder(cmdList);
pb.redirectErrorStream(true); //redirect STD ERR to STD OUT
Process process = pb.start();
try (final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line = null;
while ((line = br.readLine()) != null) {
System.out.println("std-out-line: " + line);
}
}
int outputVal = process.waitFor();
System.out.format("outputVal: %d\n", outputVal);
Since waitFor() is a blocking call, you can execute this in a separate thread or using an executorService. Sample code here:
final StringBuffer outputSb = new StringBuffer();
ExecutorService executorService = null;
try {
executorService = Executors.newSingleThreadExecutor();
final Future<Integer> future = executorService.submit(new Callable<Integer>() {
#Override
public Integer call() throws Exception {
try (final BufferedReader br = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line = null;
while ((line = br.readLine()) != null) {
outputSb.append("std-out-line: ");
outputSb.append(line);
outputSb.append('\n');
}
}
int exitValue = process.waitFor();
System.out.format("exitValue: %d\n", exitValue);
return exitValue;
}
});
while (!future.isDone()) {
System.out.println("Waiting for command to finish doing something else..");
Thread.sleep(1 * 1000);
}
int exitValue = future.get();
System.out.println("Output: " + outputSb);
} finally {
executorService.shutdown();
}
Here's a solution that uses WMIC.
public static void main( String[] args ) throws Exception {
// Vars
Process process;
String output;
// Execution
process = Runtime.getRuntime().exec("cmd /c wmic process call create calc.exe | findstr ProcessId");
output = readTrimmedOutput(process.getInputStream());
System.out.println("Output from command: " + output);
// Basic string manipulation to get process id
String str_proc_id = output.split(" = ")[1].replace(";","");
System.out.println("ProcessId is: " + str_proc_id);
// Some thread delay that you can comment/uncomment for testing if running or not
Thread.sleep(5000);
// Finding if process is still running
process = Runtime.getRuntime().exec("cmd /c wmic process get processid | findstr " + str_proc_id);
output = readTrimmedOutput(process.getInputStream());
boolean isRunning = output.contains(str_proc_id);
System.out.println("Is process still running? " + isRunning);
}
private static String readTrimmedOutput(InputStream is) throws Exception {
BufferedReader breader = new BufferedReader(new InputStreamReader(is));
String line = breader.readLine();
return line != null ? line.trim() : "";
}
Sample output
Output from command: ProcessId = 6480;
ProcessId is: 6480
Is process still running? true
For showing/displaying cmd console change some lines to:
// Execution
String your_command = "cmd.exe /c \"dir\"";
process = Runtime.getRuntime().exec("cmd /c wmic process call create \"" + your_command + "\" | findstr ProcessId");
References:
https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx
https://www.computerhope.com/wmic.htm
since I didn't quite understand what you really need,i brought a comprehensive example of openning cmd from a java class (for instance class A) and starting a process of another java class (class B) and doing some operation from class B while class B is informing class A of whether it is processing yet or not. so the whole thing is to excecute class B from command promt that class A started and sending information from class B to A to notify it that it's still running.
in my example i took Main class as class A and myProcess class as class B.
as you can see in code below the Main class is Opening cmd and is executing myProcess class then myProcess class is sending a information about the process through the socket that was created in Main class
//imports
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
//class
public class Main
{
//fields
//methods
public static void main(String[] args) throws Exception
{
Runtime run = Runtime.getRuntime();
String new_dir = "C:\\Users\\Parsa\\Desktop\\New folder (2)";//imagine the directory of myProcess.class is in this folder
startServer();
run.exec("cmd.exe /c cd \""+new_dir+"\" & start cmd.exe /k \"java myProcess\"");
}
public static void startServer()
{
Thread myThread = new Thread()
{
#Override
public void run()
{
ServerSocket ss;// creating an open port for receiving data from network
try {
ss = new ServerSocket(60010);//open port number 60010--> it can really be anything that is empty
Socket s = ss.accept();//Listens for a connection to be made to this socket and accepts it
BufferedReader in = new BufferedReader(
new InputStreamReader(s.getInputStream()));//get the inputstream and change it to a buffered reader in order to get a string from remote network
String line = null;
while ((line = in.readLine()) != null) //read the input
{
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
myThread.start();
}
}
myProcess class:
by the way you need to compile the myProcess class manually by command prompt and excecute myProcess.class file from Main class
and the myProcess class is
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
public class myProcess extends Thread
{
//field
//methods
public static void main(String[] args) throws Exception
{
System.out.println("myProcess has started");
startSender();
}
public static void startSender()
{
Thread myThread = new Thread()
{
#Override
public void run()
{
try
{
Socket s = new Socket("localhost", 60010);
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
for(int i = 0 ; i<10 ; i++)
{
out.write("Process in running");
out.newLine();
out.flush();
Thread.sleep(200);
}
out.close();
//do whatever here
System.out.println("myProcess output");
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
myThread.start();
if(!myThread.isAlive())
{
System.out.println("myProcess has finished");
}
}
}
since i didn't exactly understand what you wanted ,this is probably not exactly what you want, but... it will definitely help you if you manipulate the code.
I believe that you need to launch you application as the process and not the CMD and then launch a child process of the CMD. It is the same as in Linux.
The CMD that you launched is alive=true but when you started java from that CMD is another process which is a child of the CMD but it will not return you the expected results.
HTH,
Gal
PS. you might want to take a look at https://commons.apache.org/proper/commons-exec/ which is superior in functionality to Java in my opinion.

Compiling and executing using exec in Java fails using command that works from the command line

So the idea is that this line in the code below
Runtime.getRuntime().exec("cd /Users/fnord/Documents/workspace/LearningJava/src/PackA/; javac classA.java; cd ..; java PackA.classA");
should do the same thing as this line
cd /Users/fnord/Documents/workspace/LearningJava/src/PackA/; javac classA.java; cd ..; java PackA.classA
when that second line is run from a terminal. That is to compile and run the java code. Am I misunderstanding how exec() works? If so, what would be the best way to go about accomplishing what it is I want to accomplish?
package PackA;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
public class classA {
public static void main(String[] args) throws Exception{
ClassLoader loader = classA.class.getClassLoader();
//Sets the file path to the path of the current .java file
File file = new File(loader.getResource(classA.class.getPackage().getName()+"/"+classA.class.getSimpleName()+".class").toString().replaceAll("file:", "").replaceAll("bin", "src").replaceAll("sA.class", "sA.java"));
BufferedReader in = new BufferedReader(new FileReader(file)); //establishes the reader that will be used to read this .java file
StringBuffer string = new StringBuffer(); //the stringBuffer that will be used to hold the contents of this .java file
String stringRead = in.readLine(); //sets a string to the first line of this .java file
while((stringRead) != null){ //as long as we haven't reached the end of the file
string.append(stringRead); //append the line
string.append(System.getProperty("line.separator")); //go to the next line
stringRead = in.readLine(); //read the next line
}
Integer intToFind = new Integer(0); //the integer being looked for
if (intToFind<=10) { //as long as the intToFind is less than or equal to 10
//increment the intToFind in the stringBuffer
StringBuffer newProgram = new StringBuffer(string.toString().replaceFirst("[(]"+intToFind.toString(), "("+String.valueOf(++intToFind)));
//establishes the writer that will be used to write to the file
BufferedWriter out = new BufferedWriter(new FileWriter(file));
out.write(newProgram.toString()); //write the newProgram to this .java file with the incremented intToFind
in.close(); //close both the reader and writer
out.close();
//Go to the directory of the java file, compile the code, move down one directory, execute the .class file
Runtime.getRuntime().exec("cd /Users/fnord/Documents/workspace/LearningJava/src/PackA/; javac classA.java; cd ..; java PackA.classA");
}
}
}
cd is not a program, it's a shell command.
You could use ProcessBuilder instead, which would allow you to define the working directory context from which the command should be executed
Something like this for example
Abbriviated code from previous example, updated to provide the ability to specifiy the working directory
public int compile(String file, File workingDirectory) throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("javac", file);
pb.redirectError();
pb.directory(new File(workingDirectory));
Process p = pb.start();
InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
consumer.start();
int result = p.waitFor();
consumer.join();
System.out.println(consumer.getOutput());
return result;
}
public class InputStreamConsumer extends Thread {
private InputStream is;
private IOException exp;
private StringBuilder output;
public InputStreamConsumer(InputStream is) {
this.is = is;
}
#Override
public void run() {
int in = -1;
output = new StringBuilder(64);
try {
while ((in = is.read()) != -1) {
output.append((char) in);
}
} catch (IOException ex) {
ex.printStackTrace();
exp = ex;
}
}
public StringBuilder getOutput() {
return output;
}
public IOException getException() {
return exp;
}
}
Which you could call using something like...
compile("PackA/classA.java", new File("/Users/fnord/Documents/workspace/LearningJava/src"));
Now, if you're really courageous, you could take a look at How do you dynamically compile and load external java classes?, which uses javax.tools.JavaCompiler` class to compile a Java file...

Getting process Details in windows using Java

In windows OS. using tasklist ( getting list of current open process ) i have collected list of running process. But how to get actual path of executable file of that process [FILE LOCATION]?
Is there any way to find recently used process from java?
do you mean something like this
import java.io.*;
public class taskmanager {
public static void main(String[] args) throws IOException {
String line;
Process p = Runtime.getRuntime().exec("tasklist.exe");
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line); //<-- Parse data here.
// new lines from here
String searchPath = "where notepad.exe";
searchProcessPath(searchPath);
}
input.close();
}
public static void searchProcessPath(String processName) throws IOException
{
Runtime.getRuntime().exec(processName);
}
}

Categories