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