java shell for executing/coordinating processes? - java

I know about using Runtime.exec, you pass it a native program to run + arguments. If it's a regular program, you can run it directly. If it's a shell script, you have to run an external shell program like sh or csh or cmd.exe.
Is there some Java class (either standard or open-source) that implements a shell, meaning a program that you pass a command string or a script into, that executes commands and redirects standard I/O/err accordingly, so that you could pass a string like foo | bar > baz.out in, and it would run the foo and bar programs w/o having to run another executable outside of Java?
(and by shell I don't mean BeanShell or the standalone Rhino Javascript interpreter, those are Java implementations to execute Java and Javascript code. I'm talking about Java implementations to execute non-Java executables and handle the plumbing of redirecting I/O.)

Ok, I've worked it out:
Basically, you need to invoke bash with a "-s" and then write the full command string to it.
public class ShellExecutor {
private String stdinFlag;
private String shell;
public ShellExecutor(String shell, String stdinFlag)
{
this.shell = shell;
this.stdinFlag = stdinFlag;
}
public String execute(String cmdLine) throws IOException
{
StringBuilder sb = new StringBuilder();
Runtime run = Runtime.getRuntime();
System.out.println(shell);
Process pr = run.exec(cmdLine);
BufferedWriter bufWr = new BufferedWriter(
new OutputStreamWriter(pr.getOutputStream()));
bufWr.write(cmdLine);
try
{
pr.waitFor();
} catch (InterruptedException e) {}
BufferedReader buf = new BufferedReader(
new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line = buf.readLine()) != null)
{
sb.append(line + "\n");
}
return sb.toString();
}
}
Then use it like this:
ShellExecutor excutor = new ShellExecutor("/bin/bash", "-s");
try {
System.out.println(excutor.execute("ls / | sort -r"));
} catch (IOException e) {
e.printStackTrace();
}
Obviously, you aught to do something with the error string but this is a working example.

Since JDK 1.5 there is java.lang.ProcessBuilder which handles std and err streams as well. It's sort of the replacement for java.lang.Runtime

You've always been able to handle streams with Runtime.exec
e.g.
String cmd = "ls -al";
Runtime run = Runtime.getRuntime();
Process pr = run.exec(cmd);
pr.waitFor();
BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null) {
System.out.println(line);
}
However, if you want to put shell characters such as pipe and redirect in there you'd have to write your own command line parser which links up the streams. As far as I know there hasn't one been written. That being said, could you just invoke bash from Java with a -c "ls | sort" for example and then read the input. Hmm time to do some testing.

You can use the ProcessBuilder API provided by java.
Runtime.getRuntime().exec(...) take either an array of strings or a single string. The single-string overloads of exec() will tokenise the string into an array of arguments, before passing the string array onto one of the exec() overloads that takes a string array. The ProcessBuilder constructors, on the other hand, only take a varargs array of strings or a List of strings, where each string in the array or list is assumed to be an individual argument. Either way, the arguments obtained are then joined up into a string that is passed to the OS to execute.
Find more details at the below link
Difference between ProcessBuilder and Runtime.exec()
Sample program to execute the commands.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.List;
public class ProcessBuilderTest {
static ProcessBuilder processBuilder = null;
static Process spawnProcess = null;
static int exitValue;
static int pid;
static List<String> commands;
public static void main(String[] args) {
runSqoop();
}
public static void runSqoop() {
String[] commands = { "ssh", "node", "commands" };
processBuilder = new ProcessBuilder(commands);
try {
System.out.println("Executing " + commands.toString());
spawnProcess = processBuilder.inheritIO().start();
try {
exitValue = spawnProcess.waitFor();
pid = getPID(spawnProcess);
System.out.println("The PID is " + pid);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("Process exited with the status :" + exitValue);
} catch (Exception e) {
e.printStackTrace();
}
}
public static int getPID(Process process) {
try {
Class<?> processImplClass = process.getClass();
Field fpid = processImplClass.getDeclaredField("pid");
if (!fpid.isAccessible()) {
fpid.setAccessible(true);
}
return fpid.getInt(process);
} catch (Exception e) {
System.out.println(e.getMessage());
return -1;
}
}
}

Related

Do not get input when doing Java Compiler

I'm doing a simple java compiler. My program is running, but if it is going to scan, it will not receive input and will freeze.
A small code from my compiler
public class ProcessBuilderMultipleCommandsExample {
static String backSlashFl = "C:\\Users\\xxA\\Desktop";
public static void main(String[] args) throws InterruptedException,
IOException {
// multiple commands
// /C Carries out the command specified by string and then terminates
ProcessBuilder pbC = new ProcessBuilder( //COMPİLE
"cmd.exe", "/c", "cd " + backSlashFl + "&& javac " + "Test" + ".java");
Process processC = pbC.start();
ProcessBuilder pb = new ProcessBuilder( //RUN
"cmd.exe", "/c", "cd " + backSlashFl + "&& java " + "Test");
Process process = pb.start();
IOThreadHandler outputHandler = new IOThreadHandler(
process.getInputStream());
outputHandler.start();
process.waitFor();
System.out.println(outputHandler.getOutput());
}
private static class IOThreadHandler extends Thread {
private InputStream inputStream;
private StringBuilder output = new StringBuilder();
IOThreadHandler(InputStream inputStream) {
this.inputStream = inputStream;
}
public void run() {
Scanner br = null;
try {
br = new Scanner(new InputStreamReader(inputStream));
String line = null;
while (br.hasNextLine()) {
line = br.nextLine();
output.append(line
+ System.getProperty("line.separator"));
}
} finally {
br.close();
}
}
public StringBuilder getOutput() {
return output;
}
}
}
I think it's working in the back, but how do I get the input part?
Here's the file I want to compile and run.
import java.awt.*;
import java.awt.event.*;
import java.util.Scanner;
public class numberScan {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
System.out.println("Enter the number: ");
int a=scan.nextInt();
System.out.println("Number= " + a);
}
}
I'm waiting for your help.
Editted
Now when I run the GUI, the Run key is pressed. What do you think I should do?
buttonRun.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
String backSlashFl = file.getAbsolutePath().replace("\\", "\\\\");
backSlashFl = backSlashFl.replace(flName + ".java", "");
try {
execute("cmd.exe", "/c", "cd " + backSlashFl + " && java " + flName);
JOptionPane.showMessageDialog(null, "Dosya çalıştı!","Bilgilendirme",
JOptionPane.INFORMATION_MESSAGE);
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException ae) {
// TODO Auto-generated catch block
ae.printStackTrace();
} catch (Exception e2){
e2.printStackTrace();
}
}
});
When the GUI app runs, this run button remains.
There are several issues with the given code. I tried to squeeze it into a comment, but now decided to extend it to an answer:
The class that you want to compile is called numberScan, but obviously stored in a file called Test.java. A public class can only be stored in a file that has the same name as the class. Call the class NumberScan, and call the file NumberScan.java.
You are only trying to print the output that is provided by the input stream. But you are not printing the result that is provided by the error stream (so if there are errors, you will not see them!). Also, you only printed the output of the Process process (which is used for running the program). You did not print the output of the Process processC, which was used for compiling the program.
The reason of why you don't see any output is that the line
System.out.println(outputHandler.getOutput());
is executed before the process is finished. You used waitFor, but the output is filled by a different thread - namely, the IOThreadHandler. The getOutput method could only be called after the IOThreadHandler has finished, but if you want to continuously update the output, then this will not work.
It is not entirely clear what you want to achieve, but guessing from the code that you provided, your goal seems to be to create a program that
Compiles the given Java file
Executes the resulting Java program
Prints possible error messages and the output that is created by the program
Important: Allows interacting with the program, in the sense that it should be possible to send input to the System.in of the program.
The last two points are particularly hard to achive manually. You would have to set up threads for reading the input stream and the error stream. These would require some trickery to make sure that the data is read continuously while the program is executed. Additionally, you would have to set up a thread that forwards the data that the user enters to the Java program that is executed in its own process.
Fortunately, all this has become fairly trivial with Java 7: You can simply set an appropriate ProcessBuilder.Redirect for all the streams (namely, the redirect INHERIT), so that all the streams are mapped to the corresponding streams of the surrounding program.
Here is an example:
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.Arrays;
public class ProcessBuilderMultipleCommandsExample {
private static String path = "C:\\Users\\xxA\\Desktop";
public static void main(String[] args)
throws InterruptedException, IOException {
execute("cmd.exe", "/c", "cd " + path + " && javac " + "NumberScan" + ".java");
execute("cmd.exe", "/c", "cd " + path + " && java " + "NumberScan");
}
private static void execute(String ... commands)
throws IOException, InterruptedException
{
System.out.println("Executing "+Arrays.asList(commands));
ProcessBuilder processBuilder = new ProcessBuilder(commands);
processBuilder.redirectInput(Redirect.INHERIT);
processBuilder.redirectOutput(Redirect.INHERIT);
processBuilder.redirectError(Redirect.INHERIT);
Process process = processBuilder.start();
process.waitFor();
}
}

executing cat command from java program does not work as expected

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.

Getting output from executing a terminal command in a java code running inside Cubieboard Platform

The code that I am using for running a terminal command in Linux Debian and getting the output inside a java program is this:
public static String execute(String command) {
StringBuilder sb = new StringBuilder();
String[] commands = new String[]{"/bin/sh", "-c", command};
try {
Process proc = new ProcessBuilder(commands).start();
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(proc.getErrorStream()));
String s = null;
while ((s = stdInput.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
while ((s = stdError.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} catch (IOException e) {
return e.getMessage();
}
return sb.toString();
}
Now the problem is, it works for normal commands like ls / and gives back the appropriate result. But my goal is to run commands like:
echo 23 > /sys/class/gpio/export
which is, for example, for activating the gpio pin in the CubieBoard platform.
(Cubieboard is a mini-pc board like Raspberry Pi).
Now running this command in the terminal of the system itself, works fine and gives me the proper result. But when i am running it from this java code, i cannot get any results back.
The point is that, it works and the command executes well, but just that i cannot get the output message of the command!
For example if the pin was active from the past, then normally it should give me back the result like:
bash: echo: write error: Device or resource busy
But when i run this command through java code above, i do not get any response back.
(again it takes effect but just the response of the terminal i cannot get!)
When i run the code, both stdInput and stdError variables in the code are having the value null. :(
Please help me so that i can finish my project. this is the only part that is remaining :(
Thank you.
There maybe the childProcess doesn't run to end
Please to try:
proc.waitFor()
and run read stdInput and stdError in other Thread before proc.waitFor().
Example:
public static String execute(String command) {
String[] commands = new String[] { "/bin/sh", "-c", command };
ExecutorService executor = Executors.newCachedThreadPool();
try {
ProcessBuilder builder = new ProcessBuilder(commands);
/*-
Process proc = builder.start();
CollectOutput collectStdOut = new CollectOutput(
proc.getInputStream());
executor.execute(collectStdOut);
CollectOutput collectStdErr = new CollectOutput(
proc.getErrorStream());
executor.execute(collectStdErr);
// */
// /*-
// merges standard error and standard output
builder.redirectErrorStream();
Process proc = builder.start();
CollectOutput out = new CollectOutput(proc.getInputStream());
executor.execute(out);
// */
// child proc exit code
int waitFor = proc.waitFor();
return out.get();
} catch (IOException e) {
return e.getMessage();
} catch (InterruptedException e) {
// proc maybe interrupted
e.printStackTrace();
}
return null;
}
public static class CollectOutput implements Runnable {
private final StringBuffer buffer = new StringBuffer();
private final InputStream inputStream;
public CollectOutput(InputStream inputStream) {
super();
this.inputStream = inputStream;
}
/*
* (non-Javadoc)
*
* #see java.lang.Runnable#run()
*/
#Override
public void run() {
BufferedReader reader = null;
String line;
try {
reader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = reader.readLine()) != null) {
buffer.append(line).append('\n');
}
} catch (Exception e) {
System.err.println(e);
} finally {
try {
reader.close();
} catch (IOException e) {
}
}
}
public String get() {
return buffer.toString();
}
}
the code is right, just in the second line, I changed
"/bin/sh" to "/bin/bash"
And everything works!
sh == bash?
For a long time, /bin/sh used to point to /bin/bash on most GNU/Linux systems. As a result, it had almost become safe to ignore the difference between the two. But that started to change recently.
Some popular examples of systems where /bin/sh does not point to /bin/bash (and on some of which /bin/bash may not even exist) are:
Modern Debian and Ubuntu systems, which symlink sh to dash by default;
Busybox, which is usually run during the Linux system boot time as part of initramfs. It uses the ash shell implementation.
BSDs. OpenBSD uses pdksh, a descendant of the Korn shell. FreeBSD's sh is a descendant of the original UNIX Bourne shell.
For more information on this please refer to :
Difference between sh and bash

How to execute sh file without wait result?

I have a problem, I tried run a sh file from java code to start a JBoss Server, so when I exec this sh file I want to know when it's started and print out my console. Here is my code:
public static void runStart() {
try {
String command = "./Run.sh";
String s = get_commandline_results(command);
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("done");
}
public static String get_commandline_results(String cmd)
throws IOException, InterruptedException, IllegalCommandException {
String result = "";
Process p = null;
p = Runtime.getRuntime().exec(String.format("bash -c %s", cmd));
final ProcessResultReader stderr = new ProcessResultReader(
p.getErrorStream(), "STDERR");
final ProcessResultReader stdout = new ProcessResultReader(
p.getInputStream(), "STDOUT");
System.out.println("starting...");
stderr.start();
stdout.start();
final int exitValue = p.waitFor();// It was delayed here because sh not complete
System.out.println("started!");// How to go to here?
if (exitValue == 0) {
result = stdout.toString();
} else {
result = stderr.toString();
}
return result;
}
}
class ProcessResultReader extends Thread {
final InputStream is;
final String type;
final StringBuilder sb;
ProcessResultReader(final InputStream is, String type) {
this.is = is;
this.type = type;
this.sb = new StringBuilder();
}
public void run() {
try {
final InputStreamReader isr = new InputStreamReader(is);
final BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
this.sb.append(line).append("\n");
}
} catch (final IOException ioe) {
System.err.println(ioe.getMessage());
throw new RuntimeException(ioe);
}
}
#Override
public String toString() {
return this.sb.toString();
}
}
Thank for your help!
You have contradicting goals here: You want to continue without waiting for the script to terminate (i.e. you want JBoss running) but at the same time, you want to know the result. Short of building a time machine, this is impossible.
What you can do is you can start the script within another script start.sh:
#!/bin/bash
nohup base -c ./Run.sh > /dev/null &
Note: You can omit bash -c by making Run.sh executable.
This script will start Run.sh as a background process and exit immediately. But if JBoss can't start, you won't get an error since that will happen later.
One solution for this dilemma is a script that reads the log files of JBoss for a couple of seconds or until it sees a line of text that means "JBoss has started successfully".
To do that, use the start script and then in the Java code, start reading the log file of JBoss (you may need to wait for it to show up, first).
For this kind of trick, it's always good when you delete the old log files first, before you try to start JBoss. Otherwise, the Java program might read an old log file and continue while JBoss is still trying to start writing a new log.

How do I execute a sequence of related console commands?

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

Categories