How to invoke a Linux shell command from Java - 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.

Related

Java enter command to a process that opens a new telnet connection

I would like to write a Java program to control the Android emulator to do some testing, and now I have to take snapshots of the emulator when it is created and every time it has changes, so according to google, the command is like:
telnet localhost 5555
Trying ::1...
Connected to localhost.
Escape character is '^]'.
Android Console: type 'help' for a list of commands
OK
avd snapshot save 1
OK
So basically two commands, the first is to open a telnet connection and then enter avd snapshot save x command to save the snapshot.
However using the command like this:
public static void main(String[] args) throws IOException{
int initScore = 1000;
int ID = 0;
// monitor the log, check if a new activity is reached.
// if so, take a snapshot of the current activity and save it to the snapshot folder.
String cmd1 = "telnet localhost " + 5555;
// the static port 5557 should change to this.getVM_consolePort();
String cmd2 = "avd snapshot save " + ID;
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", cmd1, cmd2);
Process p = pb.start();
// read the process output
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
}
can only run the first command (which is to open telnet connection).
So could anyone tell me how to interact with a opened connection or enter command to another process in Java?
Here's an example using grep:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
public class Test {
public static void main(String[] args) throws IOException {
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "grep foo");
Process p = pb.start();
BufferedReader stdOut = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdErr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
Writer stdIn = new OutputStreamWriter(p.getOutputStream());
stdIn.write("foo1\nbar\nfoo2\n");
stdIn.close();
String s = null;
while ((s = stdOut.readLine()) != null) {
System.out.println(s);
}
while ((s = stdErr.readLine()) != null) {
System.out.println(s);
}
}
}
I renamed stdIn to stdOut, so they are named from the point of view of the process you're running.
I read from stderr, so that you can see any problems.
Actually I have tried this before like the code below:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
public class Main {
public static void main(String[] args) throws IOException {
// int initScore = 1000;
int ID = 0;
Process p = Runtime.getRuntime().exec("telnet localhost 5555");
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter stdIn = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
String line;
while ((line = stdInput.readLine()) != null) {
System.out.println(line);
if (line.contains("OK")){
break;
}
}
stdIn.write("avd snapshot save " + ID);
while ((line = stdInput.readLine()) != null) {
System.out.println(line);
}
}
}
Trying ::1...
Connected to localhost.
Escape character is '^]'.
Android Console: type 'help' for a list of commands
OK
I think the reason is that the telnet created a new shell and the writer is writing to the old shell, therefore it is not working, so I am looking for a solution to enter the new command into the new shell.

ProcessBuilder working in Linux but not Windows

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

Converting interactive shell output to plain text

I am trying to view the temperature table for my CPU on my Linux machine with Java. This bit of code will display the shell output for other commands, ls, cat file, but will not display watch sensors as it returns an interactive output. Is there a way I can convert it to plain text somehow?
Error: [/usr/bin/watch, sensors]
Error opening terminal: unknown.
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 tempapp{
public static void main (String args[]) throws IOException, InterruptedException {
//build command
List<String> commands = new ArrayList<String>();
commands.add("/usr/bin/watch");
//args
commands.add("sensors");
System.out.println(commands);
ProcessBuilder pb = new ProcessBuilder(commands);
pb.directory(new File("/home/ethano"));
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("\n success");
System.exit(0);
}
//weird termination
System.err.println(commands);
System.err.println(out.toString());
System.exit(1);
}
}
All that watch does is call the command it is given (sensors in this case) once every two seconds. You can simply have your application emulate this behaviour by calling /usr/bin/sensors in a for-loop once every two seconds (or however many times you need), therefore omitting the need to read interactive shell output.

One does not simply grep into ProcessBuilder

does anyone know how to use linux grep with the java ProcessBuilder? Why does this code return an empty string when it should return "sing" ?
import java.io.*;
import java.util.*;
public class Test2 {
public static void main(String[] args) throws InterruptedException,IOException{
String line;
// Initiate grep process.
ProcessBuilder pb = new ProcessBuilder("grep", "\"sing\"", "<<<\"sing\"");
Process p = pb.start();
p.waitFor();
// Get grep output:
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuilder builder = new StringBuilder();
line = null;
while ( (line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
System.out.println(result);
}
}
I also try to echo what I execute with this code:
ProcessBuilder pb = new ProcessBuilder("echo","grep", "\"sing\"", "<<<\"sing\"");
and get the correct result:
grep "sing" <<<"sing"
I finally try to execute the command at the shell and get:
sing
although it is in red font for some reason. So what am I doing wrong?
what am I doing wrong?
Something which is pretty obvious.
Do you expect, say, execve(), to understand shell constructs? No.
Well, you shouldn't be expecting ProcessBuilder to understand those either. Although it is not as low level as execve(), it is low level enough that the arguments to a command are "raw". Therefore, in your command, <<<"sing" is passed as is as an argument to grep; which means grep views it as a file to read from.
Get that in your head: what you type in the shell is interpreted by the shell; a ProcessBuilder WILL NOT use a shell to execute its processes, nor will execve(). Which, in turn, means that you cannot use shell constructs.
If you want to grep you'll have to feed your process' input with the text you want. But why use grep when Java has a builtin regex engine is another question, of course.
As to:
although it is in red font for some reason
it is simply text decoration from the grep command (well, GNU grep at least); see its manpage and the --color option. In short, in your case, it has detected that your tty had the capabilities to change the color of text and it uses that to decorate the matched text.
Try and:
echo foobar | grep foo
It will echo foobar with foo in red.
You can actually run the same command using ProcessBuilder but you have to make sure it is execute by bash. I prefer this utility method:
public static int runCmd(final String command) {
Process process=null;
int ret = 0;
String[] finalCommand = new String[] { "bash", "-c", command };
try {
final ProcessBuilder processBuilder = new ProcessBuilder(finalCommand);
processBuilder.redirectErrorStream(true);
process = processBuilder.start();
ret = process.waitFor();
// stdout+stderr
InputStreamReader isr = new InputStreamReader( process.getInputStream() );
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//System.out.println("Program terminated!");
process.destroy();
br.close();
isr.close();
}
catch (IOException|InterruptedException e) {
e.printStackTrace();
}
return ret;
}
Then call it as:
runCmd("grep -o \"sing\" <<<\"icansing\"");
And it gives me this output:
sing

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...

Categories